[alt.sources] less beta release

mark@unix386.Convergent.COM (Mark Nudelman) (09/15/89)

#! /bin/sh
# This is a shell archive.
# Remove anything before this line, then unpack it
# by saving it into a file and typing "sh file".

echo shar: Extracting \"README\"
sed "s/^X//" >'README' <<'END_OF_FILE'
X===============================================================
X===    NOTE: THIS IS THE BETA DISTRIBUTION OF less.         ===
X===          PLEASE REPORT ANY PROBLEMS TO THE AUTHOR.      ===
X===============================================================
X
XThis is the distribution of "less", a paginator similar to "more" or "pg".
XThe manual page is in less.man (nroff source in less.nro).
XMajor changes made since the last posted version are in CHANGES.
X
XINSTALLATION:
X
X1. Move the distributed source to its own directory and 
X   unpack it by running "sh" on the distribution files,
X   if you have not already done so.
X
X2. Type "sh linstall" and answer the questions it asks.
X   This will generate a makefile and a defines.h.
X
X   If you choose not to include some features in your version,
X   you may wish to edit the manual page "less.nro" and the help
X   page "less.help" to remove the references to the appropriate 
X   commands or options.
X
X3. It is a good idea to look over the generated makefile 
X   and make sure it looks ok.
X
X4. Type "make" and watch the fun.
X
X5. If the make succeeds, it will generate a program "less"
X   in your current directory.  Test the generated program.
X
X6. When satisfied that it works, if you wish to install it
X   in a public place, type "make install".
X
XIf you have any problems building or running "less", 
Xsuggestions, complaints, etc., you may mail to the 
Xauthor via USENET at:
X	sun	 \
X	uunet 	  } !pyramid!ctnews!unix386!mark
X	hplabs   /
X
X
XNote to hackers: comments noting possible improvements are enclosed
Xin double curly brackets {{ like this }}.
END_OF_FILE
echo shar: Extracting \"CHANGES\"
sed "s/^X//" >'CHANGES' <<'END_OF_FILE'
X
X	Major changes between "less" versions 97 and 123
X
X* New option (-N) causes line numbers to be displayed in the
X  text of the file (like vi "set nu").
X
X* New option (-?) prints help message immediately.
X
X* New option (-r) displays "raw" control characters, without
X  mapping them to ^X notation.
X
X* New option (-f) forces less to open non-regular files
X  (directories, etc).
X
X* New option (-k) can be used to specify lesskey files by name.
X
X* File marks (set by the m command) are now preserved when a new
X  file is edited.  The ' command can thus be used to switch files.
X
X* 8 bit characters are now supported.  A new option (-g) can be 
X  used to strip off the eighth bit (the previous behavior).
X
X* Options which take a following string (like -t) may now
X  optionally have a space between the option letter and the string.
X
X* The % command has been changed to do bracket matching, like 
X  vi % command.  The old % command is still available via p.
X  Six new commands { } ( ) [ and ] can also be used to match
X  brackets of specific types.
X
X* New commands z and w move forward/backward one window and
X  simultaneously set the window size.
X
X* New command ESC-/ searches the entire file for a pattern.
X
X* New command ESC-n repeats previous search in reverse direction.
X
X* Prompt string expansion now has %L for line number of the last
X  line in the file, and %E for the name of the editor.
X
X* New environment variable EDITPROTO can be used to tailor the
X  command string passed to the editor by the v command.
X
X* Examining a file which was previously examined will return
X  to the same position in the file.
X
X* A "%" is expanded to the current filename and a "#" to the 
X  previous filename, in both shell commands and the E command.
X  (Previously % worked only in shell commands and # worked 
X  only in the E command.)
X
X* New command ":ta" is equivalent to "-t".
X
X* New command "s" is equivalent to "-l".
X
X* Lesskey files may now use the action "toggle-option X" to
X  give an alias for toggling a specific option X.
END_OF_FILE
echo shar: Extracting \"less.nro\"
sed "s/^X//" >'less.nro' <<'END_OF_FILE'
X.TH LESS 1
X.SH NAME
Xless \- opposite of more
X.SH SYNOPSIS
X.B "less -?"
X.br
X.B "less [-[+]aABcCdeEfgimMnNqQruUsw] [-b\fIN\fB] [-h\fIN\fB] [-x\fIN\fB] [-[z]\fIN\fB]"
X.br
X.B "     [-P[mM=]\fIstring\fB] [-[lL]\fIlogfile\fB] [-k\fIkeyfile\fB]"
X.br
X.B "     [+\fIcmd\fB] [-t\fItag\fB] [\fIfilename\fB]..."
X
X.SH DESCRIPTION
X.I Less
Xis a program similar to 
X.I more
X(1), but which allows backwards movement
Xin the file as well as forward movement.
XAlso,
X.I less
Xdoes not have to read the entire input file before starting,
Xso with large input files it starts up faster than text editors like
X.I vi
X(1).
X.I Less
Xuses termcap (or terminfo on some systems),
Xso it can run on a variety of terminals.
XThere is even limited support for hardcopy terminals.
X(On a hardcopy terminal, lines which should be printed at the top
Xof the screen are prefixed with an up-arrow.)
X.PP
XCommands are based on both
X.I more
Xand
X.I vi.
XCommands may be preceded by a decimal number, 
Xcalled N in the descriptions below.
XThe number is used by some commands, as indicated.
X
X.SH COMMANDS
XIn the following descriptions, ^X means control-X.
XESC stands for the ESCAPE key; for example ESC-v means the
Xtwo character sequence "ESCAPE", then "v".
X.IP "h or H"
XHelp: display a summary of these commands.
XIf you forget all the other commands, remember this one.
X.PP
X.IP "SPACE or ^V or f or ^F"
XScroll forward N lines, default one window (see option -z below).
XIf N is more than the screen size, only the final screenful is displayed.
XWarning: some systems use ^V as a special literalization character.
X.PP
X.IP "z"
XLike SPACE, but if N is specified, it becomes the new window size.
X.PP
X.IP "RETURN or ^N or e or ^E or j or ^J"
XScroll forward N lines, default 1.
XThe entire N lines are displayed, even if N is more than the screen size.
X.PP
X.IP "d or ^D"
XScroll forward N lines, default one half of the screen size.
XIf N is specified, it becomes the new default for 
Xsubsequent d and u commands.
X.PP
X.IP "b or ^B or ESC-v"
XScroll backward N lines, default one window (see option -z below).
XIf N is more than the screen size, only the final screenful is displayed.
X.PP
X.IP "w"
XLike ESC-v, but if N is specified, it becomes the new window size.
X.PP
X.IP "y or ^Y or ^P or k or ^K"
XScroll backward N lines, default 1.
XThe entire N lines are displayed, even if N is more than the screen size.
XWarning: some systems use ^Y as a special job control character.
X.PP
X.IP "u or ^U"
XScroll backward N lines, default one half of the screen size.
XIf N is specified, it becomes the new default for 
Xsubsequent d and u commands.
X.PP
X.IP "r or ^R or ^L"
XRepaint the screen.
X.PP
X.IP R
XRepaint the screen, discarding any buffered input.
XUseful if the file is changing while it is being viewed.
X.PP
X.IP "g or < or ESC-<"
XGo to line N in the file, default 1 (beginning of file).
X(Warning: this may be slow if N is large.)
X.PP
X.IP "G or > or ESC->"
XGo to line N in the file, default the end of the file.
X(Warning: this may be slow if N is large,
Xor if N is not specified and
Xstandard input, rather than a file, is being read.)
X.PP
X.IP "p"
XGo to a position N percent into the file.
XN should be between 0 and 100.
X(This works if standard input is being read, but only if
X.I less
Xhas already read to the end of the file.
XIt is always fast, but not always useful.)
X.PP
X.IP "%"
XIf a bracket appears in the top line displayed
Xon the screen,
Xthe % command will go to the matching bracket.
XA "bracket" is one of the characters "{ } [ ] ( )".
X.PP
X.IP "{"
XIf a left curly bracket appears in the top line displayed
Xon the screen,
Xthe { command will go to the matching right curly bracket.
XIf there is more than one left curly bracket on the top line,
Xa number N may be used to specify the N-th bracket on the line.
X.PP
X.IP "}"
XIf a right curly bracket appears in the top line displayed
Xon the screen,
Xthe } command will go to the matching left curly bracket.
XIf there is more than one right curly bracket on the top line,
Xa number N may be used to specify the N-th bracket on the line.
X.PP
X.IP "("
XLike {, but applies to parentheses rather than curly brackets.
X.PP
X.IP ")"
XLike }, but applies to parentheses rather than curly brackets.
X.PP
X.IP "["
XLike {, but applies to square brackets rather than curly brackets.
X.PP
X.IP "]"
XLike }, but applies to square brackets rather than curly brackets.
X.PP
X.IP m
XFollowed by any lowercase letter, 
Xmarks the current position with that letter.
X.PP
X.IP "'"
X(Single quote.)
XFollowed by any lowercase letter, returns to the position which
Xwas previously marked with that letter.
XFollowed by another single quote, returns to the position at
Xwhich the last "large" movement command was executed.
XMarks are preserved when a new file is examined,
Xso the ' command can be used to switch between input files.
X.PP
X.IP "^X^X"
XSame as single quote.
X.PP
X.IP /pattern
XSearch forward in the file for the N-th line containing the pattern.
XN defaults to 1.
XThe pattern is a regular expression, as recognized by
X.I ed.
XThe search starts at the second line displayed
X(but see the -a option, which changes this).
X.PP
X.IP ?pattern
XSearch backward in the file for the N-th line containing the pattern.
XThe search starts at the line immediately before the top line displayed.
X.PP
X.IP "ESC-/pattern"
XSearch the entire file for the N-th line containing the pattern.
XThe search starts at the first line in the file,
Xregardless of what is displayed or the setting of the -a option.
X.PP
X.IP /!pattern
XLike /, but the search is for the N-th line
Xwhich does NOT contain the pattern.
X.PP
X.IP ?!pattern
XLike ?, but the search is for the N-th line
Xwhich does NOT contain the pattern.
X.PP
X.IP "ESC-/!pattern
XLike ESC-/, but the search is for the N-th line
Xwhich does NOT contain the pattern.
X.PP
X.IP n
XRepeat previous search, for N-th line containing the last pattern
X(or NOT containing the last pattern, if the previous search
Xwas /! or ?!).
X.PP
X.IP "ESC-n"
XRepeat previous search, but in the reverse direction.
X.PP
X.IP "E [filename]"
XExamine a new file.
XIf the filename is missing, the "current" file (see the N and P commands
Xbelow) from the list of files in the command line is re-examined.
XA percent sign (%) in the filename is replaced by the name of the
Xcurrent file.  
XA pound sign (#) is replaced by the name of the previously examined file.
X.PP
X.IP "^X^V or :e"
XSame as E.
XWarning: some systems use ^V as a special literalization character.
X.PP
X.IP "N or :n"
XExamine the next file (from the list of files given in the command line).
XIf a number N is specified (not to be confused with the command N),
Xthe N-th next file is examined.
X.PP
X.IP "P or :p"
XExamine the previous file.
XIf a number N is specified, the N-th previous file is examined.
X.PP
X.IP "= or ^G or :f"
XPrints some information about the file being viewed,
Xincluding its name
Xand the line number and byte offset of the bottom line being displayed.
XIf possible, it also prints the length of the file,
Xthe number of lines in the file
Xand the percent of the file above the last displayed line.
X.PP
X.IP \-
XFollowed by one of the command line option letters (see below),
Xthis will change the setting of that option
Xand print a message describing the new setting.
XIf the option letter has a numeric value (such as -b or -h),
Xor a string value (such as -P or -t),
Xa new value may be entered after the option letter.
X.PP
X.IP \_
X(Underscore.)
XFollowed by one of the command line option letters (see below),
Xthis will print a message describing the current setting of that option.
XThe setting of the option is not changed.
X.PP
X.IP +cmd
XCauses the specified cmd to be executed each time a new file is examined.
XFor example, +G causes 
X.I less
Xto initially display each file starting at the end 
Xrather than the beginning.
X.PP
X.IP V
XPrints the version number of 
X.I less 
Xbeing run.
X.PP
X.IP "q or :q or :Q or ZZ"
XExits
X.I less.
X.PP
XThe following 
Xtwo 
Xcommands may or may not be valid, depending on your particular installation.
X.PP
X.IP v
XInvokes an editor to edit the current file being viewed.
XThe editor is taken from the environment variable EDITOR,
Xor defaults to "vi".
XSee also the discussion of EDITPROTO under the section on PROMPTS below.
X.PP
X.IP "! shell-command"
XInvokes a shell to run the shell-command given.
XA percent sign (%) in the command is replaced by the name of the
Xcurrent file.  
XA pound sign (#) is replaced by the name of the previously examined file.
X"!!" repeats the last shell command.
X"!" with no shell command simply invokes a shell.
XIn all cases, the shell is taken from the environment variable SHELL,
Xor defaults to "sh".
X.PP
X.SH OPTIONS
XCommand line options are described below.
XMost options may be changed while
X.I less 
Xis running, via the "\-" command.
X.PP
XOptions are also taken from the environment variable "LESS".
XFor example, 
Xto avoid typing "less -options ..." each time 
X.I less 
Xis invoked, you might tell 
X.I csh:
X.sp
Xsetenv LESS "-options"
X.sp
Xor if you use 
X.I sh:
X.sp
XLESS="-options"; export LESS
X.sp
XThe environment variable is parsed before the command line,
Xso command line options override the LESS environment variable.
XIf an option appears in the LESS variable, it can be reset
Xto its default on the command line by beginning the command
Xline option with "-+".
X.sp
XA dollar sign ($) may be used to signal the end of an option string.
XThis is important only for options like -P which take a
Xfollowing string.
X.IP -?
XThis option displays a summary of the commands accepted by
X.I less
X(the same as the h command).
XIf this option is given, all other options are ignored, and
X.I less
Xexits after the help screen is viewed.
X(Depending on how your shell interprets the question mark,
Xit may be necessary to quote the question mark, thus: "-\\?".)
X.IP -a
XNormally, forward searches start just after
Xthe top displayed line (that is, at the second displayed line).
XThus, forward searches include the currently displayed screen.
XThe -a option causes forward searches to start 
Xjust after the bottom line displayed,
Xthus skipping the currently displayed screen.
X.IP -A
XThe -A option causes searches to start at the second SCREEN line
Xdisplayed, as opposed to the default which is to start at the second
XREAL line displayed.
XFor example, suppose a long real line occupies the first three screen lines.
XThe default search will start at the second real line (the fourth
Xscreen line), while the -A option
Xwill cause the search to start at the second screen line (in the
Xmidst of the first real line).
X(This option is rarely useful.)
X.IP -b
XThe -b\fIn\fR option tells
X.I less
Xto use a non-standard number of buffers.
XBuffers are 1K, and normally 10 buffers are used
X(except if data in coming from standard input; see the -B option).
XThe number \fIn\fR specifies a different number of buffers to use.
X.IP -B
XNormally, when data is coming from standard input,
Xbuffers are allocated automatically as needed, to avoid loss of data.
XThe -B option disables this feature, so that only the default number
Xof buffers are used.
XIf more data is read than will fit in the buffers, the oldest
Xdata is discarded.
X.IP -c
XNormally, 
X.I less 
Xwill repaint the screen by scrolling from the bottom of the screen.
XIf the -c option is set, when
X.I less 
Xneeds to change the entire display, it will paint from the top line down.
X.IP -C
XThe -C option is like -c, but the screen is cleared before it is repainted.
X.IP -d
XNormally,
X.I less
Xwill complain if the terminal is dumb; that is, lacks some important capability,
Xsuch as the ability to clear the screen or scroll backwards.
XThe -d option suppresses this complaint 
X(but does not otherwise change the behavior of the program on a dumb terminal).
X.IP -e
XNormally the only way to exit 
X.I less
Xis via the "q" command.
XThe -e option tells 
X.I less 
Xto automatically exit
Xthe second time it reaches end-of-file.
X.IP -E
XThe -E flag causes 
X.I less
Xto exit the first time it reaches end-of-file.
X.IP -f
XNormally,
X.I less
Xwill refuse to open a non-regular file
X(that is, a file which is a directory or a device special file).
XThe -f flag forces 
X.I less
Xto open such files.
X.IP -g
XForce input characters to 7 bits (that is, mask off the high order bit).
XThe default is to use full 8 bit characters.
X.IP -h
XNormally,
X.I less
Xwill scroll backwards when backwards movement is necessary.
XThe -h option specifies a maximum number of lines to scroll backwards.
XIf it is necessary to move backwards more than this many lines,
Xthe screen is repainted in a forward direction.
X(If the terminal does not have the ability to scroll
Xbackwards, -h0 is implied.)
X.IP -i
XThe -i option causes searches to ignore case; that is,
Xuppercase and lowercase are considered identical.
XAlso, text which is overstruck or underlined can be searched for.
X.IP -k
XThe -k option, followed immediately by a filename,
Xwill cause 
X.I less
Xto open and interpret the file as a
X.I lesskey
X(1) file.
XMultiple -k options may be specified.
XIf a file called .less exists in the user's home directory, this
Xfile is also used as a
X.I lesskey
Xfile.
X.IP -l
XThe -l option, followed immediately by a filename,
Xwill cause 
X.I less
Xto copy its input to the named file as it is being viewed.
XThis applies only when the input file is a pipe,
Xnot an ordinary file.
XIf the file already exists, 
X.I less
Xwill ask for confirmation before overwriting it.
X.IP -L
XThe -L option is like -l, but it will overwrite an existing
Xfile without asking for confirmation.
X.sp
XIf no log file has been specified,
Xthe -l and -L options can be used from within 
X.I less
Xto specify a log file.
XWithout a file name, they will simply report the name of the log file.
XThe "s" command is equivalent to specifying -l from within
X.I less.
X.IP -m
XNormally,
X.I less
Xprompts with a colon.
XThe -m option causes 
X.I less
Xto prompt verbosely (like 
X.I more),
Xwith the percent into the file.
X.IP -M
XThe -M option causes 
X.I less
Xto prompt even more verbosely than 
X.I more.
X.IP -n
XThe -n flag suppresses line numbers.
XThe default (to use line numbers) may cause
X.I less
Xto run more slowly in some cases, especially with a very large input file.
XSuppressing line numbers with the -n flag will avoid this problem.
XUsing line numbers means: the line number will be displayed in the verbose
Xprompt and in the = command,
Xand the v command will pass the current line number to the editor
X(see also the discussion of EDITPROTO in PROMPTS below).
X.IP -N
XThe -N flag causes a line number to be displayed at the beginning of
Xeach line in the display.
X.IP -P
XThe -P option provides a way to tailor the three prompt
Xstyles to your own preference.
XYou would normally put this option in your LESS environment
Xvariable, rather than type it in with each 
X.I less
Xcommand.
XSuch an option must either be the last option in the LESS variable,
Xor be terminated by a dollar sign.
X-P followed by a string changes the default (short) prompt to that string.
X-Pm changes the medium (-m) prompt to the string, and
X-PM changes the long (-M) prompt.
XAlso, -P= changes the message printed by the = command to the given string.
XAll prompt strings consist of a sequence of 
Xletters and special escape sequences.
XSee the section on PROMPTS for more details.
X.IP -q
XNormally, if an attempt is made to scroll past the end of the file
Xor before the beginning of the file, the terminal bell is rung to
Xindicate this fact.
XThe -q option tells
X.I less
Xnot to ring the bell at such times.
XIf the terminal has a "visual bell", it is used instead.
X.IP -Q
XEven if -q is given, 
X.I less 
Xwill ring the bell on certain other errors,
Xsuch as typing an invalid character.
XThe -Q option tells
X.I less
Xto be quiet all the time; that is, never ring the terminal bell.
XIf the terminal has a "visual bell", it is used instead.
X.IP -r
XNormally, control character are displayed using a carat and the
Xequivalent non-control character.
XFor example, a control-A (octal 001) is displayed as "^A".
XIf the -r flag is set, "raw" control characters are displayed,
Xwithout this translation.
XWarning: when this flag is used,
X.I less
Xcannot keep track of the actual appearance of the screen
X(since this depends on how the screen responds to
Xeach type of control character).
XThus, various display problems may result,
Xsuch as long lines being split in the wrong place.
X.IP -s
XThe -s option causes
Xconsecutive blank lines to be squeezed into a single blank line.
XThis is useful when viewing
X.I nroff
Xoutput.
X.IP -t
XThe -t option, followed immediately by a TAG,
Xwill edit the file containing that tag.
XFor this to work, there must be a file called "tags" in the
Xcurrent directory, which was previously built by the 
X.I ctags
X(1) command.
XThis option may also be specified from within 
X.I less 
X(using the \- command) as a way of examining a new file.
XFor
X.I vi
Xcompatibility, the command ":ta" is equivalent to specifying -t from within
X.I less.
X.IP -u
XIf the -u option is given, 
Xbackspaces are treated as printable characters;
Xthat is, they are sent to the terminal when they appear in the input.
X.IP -U
XIf the -U option is given,
Xbackspaces are printed as the two character sequence "^H".
X.sp
XIf neither -u nor -U is given,
Xbackspaces which appear adjacent to an underscore character
Xare treated specially:
Xthe underlined text is displayed 
Xusing the terminal's hardware underlining capability.
XAlso, backspaces which appear between two identical characters
Xare treated specially: 
Xthe overstruck text is printed 
Xusing the terminal's hardware boldface capability.
XOther backspaces are deleted, along with the preceding character.
X.IP -w
XNormally,
X.I less
Xuses a tilde character to represent lines past the end of the file.
XThe -w option causes blank lines to be used instead.
X.IP -x
XThe -x\fIn\fR option sets tab stops every \fIn\fR positions.
XThe default for \fIn\fR is 8.
X.IP -[z]
XWhen given a backwards or forwards window command,
X.I less
Xwill by
Xdefault scroll backwards or forwards one screenful of lines. 
XThe -z\fIn\fR option changes the default scrolling window size 
Xto \fIn\fR lines.
XThe z and w commands can also be used to change the window size.
XNote that the "z" in "-z\fIn\fR" is optional for compatibility with
X.I more.
X.IP +
XIf a command line option begins with \fB+\fR,
Xthe remainder of that option is taken to be an initial command to
X.I less.
XFor example, +G tells
X.I less
Xto start at the end of the file rather than the beginning,
Xand +/xyz tells it to start at the first occurrence of "xyz" in the file.
XAs a special case, +<number> acts like +<number>g; 
Xthat is, it starts the display at the specified line number
X(however, see the caveat under the "g" command above).
XIf the option starts with \fB++\fR, the initial command applies to
Xevery file being viewed, not just the first one.
XThe + command described previously
Xmay also be used to set (or change) an initial command for every file.
X
X.SH "KEY BINDINGS"
XYou may define your own 
X.I less
Xcommands by using the program 
X.I lesskey
X(1)
Xto create a file called ".less" in your home directory.
XThis file specifies a set of command keys and an action
Xassociated with each key.
XSee the
X.I lesskey
Xmanual page for more details.
X
X.SH "PROMPTS"
XThe -P option allows you to tailor the prompt to your preference.
XThe string given to the -P option replaces the specified prompt string.
XCertain characters in the string are interpreted specially.
XThe prompt mechanism is rather complicated to provide flexibility,
Xbut the ordinary user need not understand the details of constructing
Xpersonalized prompt strings.
X.sp
XA percent sign followed by a single character is expanded
Xaccording to what the following character is:
X.IP "%bX"
XReplaced by the byte offset into the current input file.
XThe b is followed by a single character (shown as X above)
Xwhich specifies the line whose byte offset is to be used.
XIf the character is a "t", the byte offset of the top line in the
Xdisplay is used,
Xan "m" means use the middle line,
Xa "b" means use the bottom line,
Xand a "B" means use the line just after the bottom line.
X.IP "%E"
XReplaced by the name of the editor (from the EDITOR environment variable).
XSee the discussion of the EDITPROTO feature below.
X.IP "%f"
XReplaced by the name of the current input file.
X.IP "%i"
XReplaced by the index of the current file in the list of
Xinput files.
X.IP "%lX"
XReplaced by the line number of a line in the input file.
XThe line to be used is determined by the X, as with the %b option.
X.IP "%L"
XReplaced by the line number of the last line in the input file.
X.IP "%m"
XReplaced by the total number of input files.
X.IP "%pX"
XReplaced by the percent into the current input file.
XThe line used is determined by the X as with the %b option.
X.IP "%s"
XReplaced by the size of the current input file.
X.IP "%t"
XCauses any trailing spaces to be removed.
XUsually used at the end of the string, but may appear anywhere.
X.IP "%x"
XReplaced by the name of the next input file in the list.
X.PP
XIf any item is unknown (for example, the file size if input
Xis a pipe), a question mark is printed instead.
X.PP
XThe format of the prompt string can be changed
Xdepending on certain conditions.
XA question mark followed by a single character acts like an "IF":
Xdepending on the following character, a condition is evaluated.
XIf the condition is true, any characters following the question mark
Xand condition character, up to a period, are included in the prompt.
XIf the condition is false, such characters are not included.
XA colon appearing between the question mark and the
Xperiod can be used to establish an "ELSE": any characters between
Xthe colon and the period are included in the string if and only if
Xthe IF condition is false.
XCondition characters (which follow a question mark) may be:
X.IP "?a"
XTrue if any characters have been included in the prompt so far.
X.IP "?bX"
XTrue if the byte offset of the specified line is known.
X.IP "?e"
XTrue if at end-of-file.
X.IP "?f"
XTrue if there is an input filename
X(that is, if input is not a pipe).
X.IP "?lX"
XTrue if the line number of the specified line is known.
X.IP "?L"
XTrue if the line number of the last line in the file is known.
X.IP "?m"
XTrue if there is more than one input file.
X.IP "?n"
XTrue if this is the first prompt in a new input file.
X.IP "?pX"
XTrue if the percent into the current input file
Xof the specified line is known.
X.IP "?s"
XTrue if the size of current input file is known.
X.IP "?x"
XTrue if there is a next input file
X(that is, if the current input file is not the last one).
X.PP
XAny characters other than the special ones
X(question mark, colon, period, percent, and backslash)
Xbecome literally part of the prompt.
XAny of the special characters may be included in the prompt literally
Xby preceding it with a backslash.
X.PP
XSome examples:
X.sp
X?f%f:Standard input.
X.sp
XThis prompt prints the filename, if known;
Xotherwise the string "Standard input".
X.sp
X?f%f .?ltLine %lt:?pt%pt\\%:?btByte %bt:-...
X.sp
XThis prompt would print the filename, if known.
XThe filename is followed by the line number, if known,
Xotherwise the percent if known, otherwise the byte offset if known.
XOtherwise, a dash is printed.
XNotice how each question mark has a matching period,
Xand how the % after the %pt
Xis included literally by escaping it with a backslash.
X.sp
X?n?f%f\ .?m(file\ %i\ of\ %m)\ ..?e(END)\ ?x-\ Next\\:\ %x..%t
X.sp
XThis prints the filename if this is the first prompt in a file,
Xfollowed by the "file N of N" message if there is more
Xthan one input file.
XThen, if we are at end-of-file, the string "(END)" is printed
Xfollowed by the name of the next file, if there is one.
XFinally, any trailing spaces are truncated.
XThis is the default prompt.
XFor reference, here are the defaults for
Xthe other two prompts (-m and -M respectively).
XEach is broken into two lines here for readability only.
X.nf
X.sp
X?n?f%f\ .?m(file\ %i\ of\ %m)\ ..?e(END)\ ?x-\ Next\\:\ %x.:
X	?pB%pB\\%:byte\ %bB?s/%s...%t
X.sp
X?f%f\ .?n?m(file\ %i\ of\ %m)\ ..?ltline\ %lt?L/%L.\ :byte\ %bB?s/%s.\ .
X	?e(END)\ ?x-\ Next\\:\ %x.:?pB%pB\\%..%t
X.sp
X.fi
XAnd here is the default message produced by the = command:
X.nf
X.sp
X?f%f\ .?m(file\ %i\ of\ %m)\ .?ltline\ %lt?L/%L.\ .
X	byte\ %bB?s/%s.\ ?e(END)\ :?pB%pB\\%..%t
X.fi
X.PP
XThe prompt expansion features are also used for another purpose:
Xif an environment variable EDITPROTO is defined, it is used
Xas the command to be executed when the v command is invoked.
XThe EDITPROTO string is expanded in the same way as the prompt strings.
XThe default value for EDITPROTO is:
X.nf
X	%E\ ?lm+%lm.\ %f
X.fi
XNote that this expands to the editor name, followed by a + and the
Xline number, followed by the file name.
XIf your editor does not accept the "+linenumber" syntax, or has other
Xdifferences in invocation syntax, the EDITPROTO variable can be 
Xchanged to modify this default.
X
X.SH "ENVIRONMENT VARIABLES"
X.IP EDITOR
XThe name of the editor (used for the v command).
X.IP EDITPROTO
XEditor prototype string (used for the v command).
XSee discussion under PROMPTS.
X.IP HOME
XName of the user's home directory (used to find a .less file).
X.IP LESS
XFlags which are passed to 
X.I less
Xautomatically.
X.IP SHELL
XThe shell used to execute the ! command, as well as to expand filenames.
X.IP TERM
XThe type of terminal on which
X.I less
Xis being run.
X
X.SH "SEE ALSO"
Xlesskey(1)
X
X.SH WARNINGS
XThe = command and prompts (unless changed by -P)
Xreport the line number of the line at the top of the screen,
Xbut the byte and percent of the line at the bottom of the screen.
END_OF_FILE
echo shar: Extracting \"lesskey.nro\"
sed "s/^X//" >'lesskey.nro' <<'END_OF_FILE'
X.TH LESSKEY 1
X.SH NAME
Xlesskey \- specify key bindings for less
X.SH SYNOPSIS
X.B "lesskey [-o output] [input]"
X.SH DESCRIPTION
X.I Lesskey
Xis used to specify a set of key bindings to be used by 
X.I less.
XThe input file is a text file which describes the key bindings,
Xand the output file is a binary file which is used by 
X.I less.
XIf no input file is specified, standard input is used.
XIf no output file is specified, $HOME/.less is used.
X.PP
XThe input file consists of lines of the form:
X.sp
X	string <whitespace> action <newline>
X.sp
XWhitespace is any sequence of one or more spaces and/or tabs.
XThe "string" is the command key(s) which invoke the action.
XThe string may be a single command key, or a sequence of up to 15 keys.
XThe "action" is the name of the less action, from the list below.
XThe characters in the "string" may appear literally, or be
Xprefixed by a carat to indicate a control key.
XA backslash may be used to cause the following character
Xto be taken literally.
XCharacters which must be preceded by backslash include
Xcarat, space, tab and the backslash itself.
XA backslash followed by one to three octal digits may be used to
Xspecify a character by its octal value.
XBlank lines and lines which start with a pound sign (#) are ignored.
X.PP
XA special case is the "toggle-option" action.
XThis action may be followed by a single letter indicating the
Xoption to be toggled.
XSee the ":ta" command in the example below.
X.PP
XAs an example, the following input file describes the set of
Xdefault command keys used by less:
X.sp
X.nf
X	k			back-line
X	y			back-line
X	^K			back-line
X	^Y			back-line
X	^P			back-line
X	b			back-screen
X	^B			back-screen
X	\\33v			back-screen
X	u			back-scroll
X	^U			back-scroll
X	?			back-search
X	w			back-window
X	{			bracket-{
X	}			bracket-}
X	(			bracket-(
X	)			bracket-)
X	[			bracket-[
X	]			bracket-]
X	%			bracket-any
X	E			examine
X	:e			examine
X	^X^V			examine
X	\\33/			file-search
X	+			first-cmd
X	e			forw-line
X	j			forw-line
X	^E			forw-line
X	^J			forw-line
X	^M			forw-line
X	^N			forw-line
X	f			forw-screen
X	^F			forw-screen
X	\\40			forw-screen
X	^V			forw-screen
X	d			forw-scroll
X	^D			forw-scroll
X	/			forw-search
X	z			forw-window
X	G			goto-end
X	>			goto-end
X	\\33>			goto-end
X	g			goto-line
X	<			goto-line
X	\\33<			goto-line
X	'			goto-mark
X	^X^X			goto-mark
X	h			help
X	H			help
X	N			next-file
X	:n			next-file
X	p			percent
X	P			prev-file
X	:p			prev-file
X	q			quit
X	:q			quit
X	:Q			quit
X	ZZ			quit
X	^L			repaint
X	^R			repaint
X	r			repaint
X	R			flush-repaint
X	n			repeat-search
X	\\33n			reverse-search
X	m			set-mark
X	!			shell
X	=			status
X	^G			status
X	:f			status
X	-			toggle-option
X	:ta			toggle-option t
X	s			toggle-option l
X	_			display-option
X	V			version
X	v			visual
X
X.fi
X.sp
XCommands specified by
X.I lesskey
Xtake precedence over the default commands.
XA default command key may be disabled by including it in the
Xkey file with the action "invalid".
X
X.SH "SEE ALSO"
Xless(1)
END_OF_FILE
echo shar: Extracting \"vecho.c\"
sed "s/^X//" >'vecho.c' <<'END_OF_FILE'
X/*
X * This dumb little program emulates the System V "echo" command,
X * to accommodate BSD systems which don't understand the \c escape,
X * meaning don't echo a newline.  BSD uses "echo -n".
X */
X
X#include <stdio.h>
X
Xint putnl;
X
Xmain(argc, argv)
X	int argc;
X	char *argv[];
X{
X	putnl = 1;
X	while (--argc > 0)
X	{
X		vecho(*++argv);
X		if (argc > 1)
X			putchar(' ');
X	}
X	if (putnl)
X		putchar('\n');
X	exit(0);
X}
X
Xvecho(str)
X	char *str;
X{
X	register char *s;
X
X	for (s = str;  *s != '\0';  s++)
X	{
X		if (*s == '\\' && s[1] == 'c')
X		{
X			putnl = 0;
X			return;
X		}
X		putchar(*s);
X	}
X}
END_OF_FILE
echo shar: Extracting \"mkfuncs.awk\"
sed "s/^X//" >'mkfuncs.awk' <<'END_OF_FILE'
XBEGIN { FS="("; state = 0 }
X
X/^	public/ { ftype = $0; state = 1 }
X
X{ if (state == 1)
X	state = 2
X  else if (state == 2)
X	{ print ftype,$1,"();"; state = 0 }
X}
END_OF_FILE
echo shar: Extracting \"less.help\"
sed "s/^X//" >'less.help' <<'END_OF_FILE'
X
X                     SUMMARY OF COMMANDS
X
X      Commands marked with * may be preceded by a number, N.
X      Notes in parentheses indicate the behavior if N is given.
X
X  h  H                 Display this help.
X  q  :q  :Q  ZZ        Exit.
X
X  e  ^E  j  ^N  CR  *  Forward  one line (or N lines).
X  y  ^Y  k  ^K  ^P  *  Backward one line (or N lines).
X  f  ^F  ^V  SPACE  *  Forward  one window (or N lines).
X  b  ^B  ESC-v      *  Backward one window (or N lines).
X  z                 *  Forward  one window (and set window to N).
X  w                 *  Backward one window (and set window to N).
X  d  ^D             *  Forward  one half-window (and set half-window to N).
X  u  ^U             *  Backward one half-window (and set half-window to N).
X  r  ^R  ^L            Repaint screen.
X  R                    Repaint screen, discarding buffered input.
X
X  NOTE: default "window" is the screen height.
X        default "half-window" is half of the screen height.
X
X  /pattern          *  Search forward for (N-th) matching line.
X  ?pattern          *  Search backward for (N-th) matching line.
X  ESC-/pattern      *  Search from top of file for (N-th) matching line.
X
X  /!pattern         *  Search forward for (N-th) NON-matching line.
X  ?!pattern         *  Search backward for (N-th) NON-matching line.
X  ESC-/!pattern     *  Search from top of file for (N-th) NON-matching line.
X
X  n                 *  Repeat previous search (for N-th occurrence).
X  ESC-n             *  Repeat previous search in reverse direction.
X
X  g  <  ESC-<       *  Go to first line in file (or line N).
X  G  >  ESC->       *  Go to last line in file (or line N).
X  p                 *  Go to beginning of file (or N percent into file).
X  %                    Go to a matching bracket.
X  {                 *  Go to the } which matches the (N-th) { in the top line.
X  }                 *  Go to the { which matches the (N-th) } in the top line.
X  (                 *  Go to the ) which matches the (N-th) ( in the top line.
X  )                 *  Go to the ( which matches the (N-th) ) in the top line.
X  [                 *  Go to the ] which matches the (N-th) [ in the top line.
X  ]                 *  Go to the [ which matches the (N-th) ] in the top line.
X  m<letter>            Mark the current position with <letter>.
X  '<letter>            Go to a previously marked position.
X  ''                   Go to the previous position.
X  ^X^X                 Same as '.
X
X  E [file]             Examine a new file.
X  :e  ^X^V             Same as E.
X  :n  N             *  Examine the (N-th) next file from the command line.
X  :p  P             *  Examine the (N-th) previous file from the command line.
X  =  ^G  :f            Print current file name.
X  V                    Print version number of "less".
X
X  -<flag>              Toggle a command line flag [see FLAGS below].
X  _<flag>              Display the setting of a command line flag.
X  +cmd                 Execute the less cmd each time a new file is examined.
X
X  !command             Passes the command to $SHELL to be executed.
X  v                    Edit the current file with $EDITOR.
X
X
X                         FLAGS
X
X        Most flags may be changed either on the command line,
X        or from within less by using the - command.
X
X  -a  -A        Set forward search starting location.
X  -b [N]        Number of buffers.
X  -B            Automatically allocate buffers.
X  -c  -C        Repaint by scrolling/clearing.
X  -d            Dumb terminal.
X  -e  -E        Quit at end of file.
X  -f            Force open non-regular files.
X  -g            Use 7 bit characters.
X  -h [N]        Backwards scroll limit.
X  -i            Ignore case in searches.
X  -k [file]     Use a lesskey file.
X  -l [file]     Log file.
X  -L [file]     Log file (unconditionally overwrite).
X  -m  -M        Set prompt style.
X  -n  -N        Use line numbers.
X  -P [prompt]   Define new prompt.
X  -q  -Q        Quiet the terminal bell.
X  -r            Translate control characters.
X  -s            Squeeze multiple blank lines.
X  -t [tag]      Find a tag.
X  -u  -U        Change handling of backspaces.
X  -w            Display ~ for lines after end-of-file.
X  -x [N]        Set tab stops.
X  -z [N]        Set size of window.
X
END_OF_FILE
echo shar: Extracting \"less.h\"
sed "s/^X//" >'less.h' <<'END_OF_FILE'
X/*
X * Standard include file for "less".
X */
X
X/*
X * Include the file of compile-time options.
X */
X#include "defines.h"
X
X/*
X * Language details.
X */
X#if !VOID
X#define	void  int
X#endif
X#define	public		/* PUBLIC FUNCTION */
X
X/*
X * Special types and constants.
X */
Xtypedef long		POSITION;
X/*
X * {{ Warning: if POSITION is changed to other than "long",
X *    you may have to change some of the printfs which use "%ld"
X *    to print a variable of type POSITION. }}
X */
X
X#define	NULL_POSITION	((POSITION)(-1))
X
X/*
X * The type of signal handler functions.
X * Usually int, although it should be void.
X */
Xtypedef	int		HANDLER;
X
X/*
X * The type of a file handle.
X * (Unfortunate name: HANDLE has nothing to do with HANDLER!)
X */
Xtypedef	int		HANDLE;
X
X#define	FILENAME	128	/* Max size of a filename */
X
X#define	EOI		(0)
X
X#ifndef NULL
X#define	NULL		(0)
X#endif
X
X#define	READ_INTR	(-2)
X
X/* How quiet should we be? */
X#define	NOT_QUIET	0	/* Ring bell at eof and for errors */
X#define	LITTLE_QUIET	1	/* Ring bell only for errors */
X#define	VERY_QUIET	2	/* Never ring bell */
X
X/* How should we prompt? */
X#define	PR_SHORT	0	/* Prompt with colon */
X#define	PR_MEDIUM	1	/* Prompt with message */
X#define	PR_LONG		2	/* Prompt with longer message */
X
X/* How should we handle backspaces? */
X#define	BS_SPECIAL	0	/* Do special things for underlining and bold */
X#define	BS_NORMAL	1	/* \b treated as normal char; actually output */
X#define	BS_CONTROL	2	/* \b treated as control char; prints as ^H */
X
X/* How should we search? */
X#define	SRCH_FORW	0	/* Search forward from current position */
X#define	SRCH_BACK	1	/* Search backward from current position */
X#define	SRCH_FILE	2	/* Search forward from beginning of file */
X#define	SRCH_NOMATCH	0100	/* Search for non-matching lines */
X#define	SRCH_TYPE(t)	((t) & 077)
X#define	SRCH_FLAG(t)	((t) & 0100)
X
X/* Special chars used to tell put_line() to do something special */
X#define	CARATBIT	(0x100)
X#define	UL_CHAR		(0x201)	/* Enter underline mode */
X#define	UE_CHAR		(0x202)	/* Exit underline mode */
X#define	BO_CHAR		(0x203)	/* Enter boldface mode */
X#define	BE_CHAR		(0x204)	/* Exit boldface mode */
X
X#define	CONTROL(c)		((c)&037)
X#define	SIGNAL(sig,func)	signal(sig,func)
X
X/* Library function declarations */
Xoffset_t lseek();
X#define	BAD_LSEEK	((offset_t)-1)
Xchar *calloc(), *ecalloc();
X
X#include "funcs.h"
END_OF_FILE
echo shar: Extracting \"position.h\"
sed "s/^X//" >'position.h' <<'END_OF_FILE'
X/*
X * Include file for interfacing to position.c modules.
X */
X#define	TOP		(0)
X#define	TOP_PLUS_ONE	(1)
X#define	BOTTOM		(-1)
X#define	BOTTOM_PLUS_ONE	(-2)
X#define	MIDDLE		(-3)
END_OF_FILE
echo shar: Extracting \"funcs.h\"
sed "s/^X//" >'funcs.h' <<'END_OF_FILE'
X	public void end_logfile ();
X	public void sync_logfile ();
X	public int ch_seek ();
X	public int ch_end_seek ();
X	public int ch_beg_seek ();
X	public POSITION ch_length ();
X	public POSITION ch_tell ();
X	public int ch_forw_get ();
X	public int ch_back_get ();
X	public void ch_init ();
X	public void commands ();
X	public int cmd_decode ();
X	public void noprefix ();
X	public int add_cmdtable ();
X	public void add_hometable ();
X	public void help ();
X	public POSITION forw_line ();
X	public POSITION back_line ();
X	public void prewind ();
X	public void plinenum ();
X	public int pappend ();
X	public int gline ();
X	public void null_line ();
X	public POSITION forw_raw_line ();
X	public POSITION back_raw_line ();
X	public void clr_linenum ();
X	public void add_lnum ();
X	public int find_linenum ();
X	public POSITION find_pos ();
X	public int currline ();
X	public void edit ();
X	public void next_file ();
X	public void prev_file ();
X	public void strtcpy ();
X	public char * save ();
X	public char * ecalloc ();
X	public char * skipsp ();
X	public void quit ();
X	public void scan_option ();
X	public void toggle_option ();
X	public int single_char_option ();
X	public char * opt_prompt ();
X	public int isoptpending ();
X	public void nopendopt ();
X	public int getnum ();
X	public void opt_l ();
X	public void opt__L ();
X	public void opt_k ();
X	public void opt_t ();
X	public void opt_P ();
X	public void opt_b ();
X	public void opt_g ();
X	public void opt_query ();
X	public void init_option ();
X	public struct option * findopt ();
X	public void lsystem ();
X	public int iread ();
X	public void intread ();
X	public long get_time ();
X	public char * fexpand ();
X	public char * glob ();
X	public char * glob ();
X	public char * bad_file ();
X	public POSITION filesize ();
X	public char * bad_file ();
X	public POSITION filesize ();
X	public char * errno_message ();
X	public char * errno_message ();
X	public void put_line ();
X	public int control_char ();
X	public int carat_char ();
X	public void flush ();
X	public void dropout ();
X	public void putchr ();
X	public void putstr ();
X	public void error ();
X	public void ierror ();
X	public POSITION position ();
X	public void add_forw_pos ();
X	public void add_back_pos ();
X	public void pos_clear ();
X	public void pos_init ();
X	public int onscreen ();
X	public HANDLE get_handle ();
X	public void save_handle ();
X	public char * get_filename ();
X	public void store_pos ();
X	public POSITION recall_pos ();
X	public void forward ();
X	public void backward ();
X	public void repaint ();
X	public void jump_forw ();
X	public void jump_back ();
X	public void jump_loc ();
X	public void jump_percent ();
X	public void init_mark ();
X	public void setmark ();
X	public void lastmark ();
X	public void gomark ();
X	public int get_back_scroll ();
X	public void match_brac ();
X	public void search ();
X	public void init_prompt ();
X	public char * pr_expand ();
X	public char * eq_message ();
X	public char * pr_string ();
X	public void raw_mode ();
X	public void get_term ();
X	public void init ();
X	public void deinit ();
X	public void home ();
X	public void add_line ();
X	public void lower_left ();
X	public void bell ();
X	public void vbell ();
X	public void clear ();
X	public void clear_eol ();
X	public void so_enter ();
X	public void so_exit ();
X	public void ul_enter ();
X	public void ul_exit ();
X	public void bo_enter ();
X	public void bo_exit ();
X	public void backspace ();
X	public void putbs ();
X	public HANDLER winch ();
X	public HANDLER winch ();
X	public void init_signals ();
X	public void psignals ();
X	public int findtag ();
X	public int tagsearch ();
X	public void open_getchr ();
X	public int getchr ();
END_OF_FILE
echo shar: Extracting \"cmd.h\"
sed "s/^X//" >'cmd.h' <<'END_OF_FILE'
X#define	MAX_USERCMD		200
X#define	MAX_CMDLEN		16
X
X#define	A_AGAIN_SEARCH		1
X#define	A_B_LINE		2
X#define	A_B_SCREEN		3
X#define	A_B_SCROLL		4
X#define	A_B_SEARCH		5
X#define	A_DIGIT			6
X#define	A_DISP_OPTION		7
X#define	A_DEBUG			8
X#define	A_EXAMINE		9
X#define	A_FIRSTCMD		10
X#define	A_FREPAINT		11
X#define	A_F_LINE		12
X#define	A_F_SCREEN		13
X#define	A_F_SCROLL		14
X#define	A_F_SEARCH		15
X#define	A_GOEND			16
X#define	A_GOLINE		17
X#define	A_GOMARK		18
X#define	A_HELP			19
X#define	A_NEXT_FILE		20
X#define	A_PERCENT		21
X#define	A_PREFIX		22
X#define	A_PREV_FILE		23
X#define	A_QUIT			24
X#define	A_REPAINT		25
X#define	A_SETMARK		26
X#define	A_SHELL			27
X#define	A_STAT			28
X#define	A_REVERSE_SEARCH	29
X#define	A_TOGGLE_OPTION		30
X#define	A_VERSION		31
X#define	A_VISUAL		32
X#define	A_F_WINDOW		33
X#define	A_B_WINDOW		34
X#define	A_T_SEARCH		35
X#define	A_ANYBRAC		36
X#define	A_BRACLP		37
X#define	A_BRACRP		38
X#define	A_BRACLC		39
X#define	A_BRACRC		40
X#define	A_BRACLS		41
X#define	A_BRACRS		42
X
X#define	A_INVALID		100
X#define	A_NOACTION		101
X
X#define	A_1_TOGGLE_OPTION	(0200)
END_OF_FILE
echo shar: Extracting \"option.h\"
sed "s/^X//" >'option.h' <<'END_OF_FILE'
X#define	END_OPTION_STRING	('$')
X
X/*
X * Types of options.
X */
X#define	BOOL		01	/* Boolean option: 0 or 1 */
X#define	TRIPLE		02	/* Triple-valued option: 0, 1 or 2 */
X#define	NUMBER		04	/* Numeric option */
X#define	STRING		010	/* String-valued option */
X#define	NOVAR		020	/* No associated variable */
X#define	REPAINT		040	/* Repaint screen after toggling option */
X#define	NO_TOGGLE	0100	/* Option cannot be toggled with "-" cmd */
X
X#define	OTYPE		(BOOL|TRIPLE|NUMBER|STRING|NOVAR)
X
X/*
X * Argument to a handling function tells what type of activity:
X */
X#define	INIT	0	/* Initialization (from command line) */
X#define	QUERY	1	/* Query (from _ or - command) */
X#define	TOGGLE	2	/* Change value (from - command) */
X
Xstruct option
X{
X	char oletter;		/* The controlling letter (a-z) */
X	char otype;		/* Type of the option */
X	int odefault;		/* Default value */
X	int *ovar;		/* Pointer to the associated variable */
X	void (*ofunc)();	/* Pointer to special handling function */
X	char *odesc[3];		/* Description of each value */
X};
X
END_OF_FILE
echo shar: Extracting \"lesskey.c\"
sed "s/^X//" >'lesskey.c' <<'END_OF_FILE'
X/*
X *	lesskey [-o output] [input]
X *
X *	Make a .less file.
X *	If no input file is specified, standard input is used.
X *	If no output file is specified, $HOME/.less is used.
X *
X *	The .less file is used to specify (to "less") user-defined
X *	key bindings.  Basically any sequence of 1 to MAX_CMDLEN
X *	keystrokes may be bound to an existing less function.
X *
X *	The input file is an ascii file consisting of a 
X *	sequence of lines of the form:
X *		string <whitespace> action <newline>
X *
X *	"string" is a sequence of command characters which form
X *		the new user-defined command.  The command
X *		characters may be:
X *		1. The actual character itself.
X *		2. A character preceded by ^ to specify a
X *		   control character (e.g. ^X means control-X).
X *		3. Any character (other than an octal digit) preceded by
X *		   a \ to specify the character itself (characters which
X *		   must be preceded by \ include ^, \, and whitespace.
X *		4. A backslash followed by one to three octal digits
X *		   to specify a character by its octal value.
X *	"action" is the name of a "less" action, from the table below.
X *
X *	Blank lines and lines which start with # are ignored.
X *
X *
X *	The output file is a non-ascii file, consisting of
X *	zero or more byte sequences of the form:
X *		string <0> <action>
X *
X *	"string" is the command string.
X *	"<0>" is one null byte.
X *	"<action>" is one byte containing the action code (the A_xxx value).
X *
X *
X *		Revision history
X *
X *	v1: Initial version.					10/13/87  mark
X */
X
X#include <stdio.h>
X#include "less.h"
X#include "cmd.h"
X
Xchar usertable[MAX_USERCMD];
X
Xstruct cmdname
X{
X	char *cn_name;
X	int cn_action;
X} cmdnames[] = 
X{
X	"back-line",		A_B_LINE,
X	"back-screen",		A_B_SCREEN,
X	"back-scroll",		A_B_SCROLL,
X	"back-search",		A_B_SEARCH,
X	"back-window",		A_B_WINDOW,
X	"bracket-{",		A_BRACLC,
X	"bracket-}",		A_BRACRC,
X	"bracket-(",		A_BRACLP,
X	"bracket-)",		A_BRACRP,
X	"bracket-[",		A_BRACLS,
X	"bracket-]",		A_BRACRS,
X	"bracket-any",		A_ANYBRAC,
X	"debug",		A_DEBUG,
X	"display-flag",		A_DISP_OPTION,
X	"display-option",	A_DISP_OPTION,
X	"end",			A_GOEND,
X	"examine",		A_EXAMINE,
X	"file-search",		A_T_SEARCH,
X	"first-cmd",		A_FIRSTCMD,
X	"firstcmd",		A_FIRSTCMD,
X	"flush-repaint",	A_FREPAINT,
X	"forw-line",		A_F_LINE,
X	"forw-screen",		A_F_SCREEN,
X	"forw-scroll",		A_F_SCROLL,
X	"forw-search",		A_F_SEARCH,
X	"forw-window",		A_F_WINDOW,
X	"goto-end",		A_GOEND,
X	"goto-line",		A_GOLINE,
X	"goto-mark",		A_GOMARK,
X	"help",			A_HELP,
X	"invalid",		A_NOACTION,
X	"next-file",		A_NEXT_FILE,
X	"noaction",		A_NOACTION,
X	"percent",		A_PERCENT,
X	"prev-file",		A_PREV_FILE,
X	"quit",			A_QUIT,
X	"repaint",		A_REPAINT,
X	"repaint-flush",	A_FREPAINT,
X	"repeat-search",	A_AGAIN_SEARCH,
X	"reverse-search",	A_REVERSE_SEARCH,
X	"set-mark",		A_SETMARK,
X	"shell",		A_SHELL,
X	"status",		A_STAT,
X	"toggle-flag",		A_TOGGLE_OPTION,
X	"toggle-option",	A_TOGGLE_OPTION,
X	"version",		A_VERSION,
X	"visual",		A_VISUAL,
X	NULL,			0
X};
X
Xmain(argc, argv)
X	int argc;
X	char *argv[];
X{
X	char *p;		/* {{ Can't be register since we use &p }} */
X	register char *up;	/* Pointer into usertable */
X	FILE *desc;		/* Description file (input) */
X	FILE *out;		/* Output file */
X	int linenum;		/* Line number in input file */
X	char *currcmd;		/* Start of current command string */
X	int errors;
X	int i, j;
X	char line[100];
X	char *outfile;
X
X	extern char *getenv();
X
X	/*
X	 * Process command line arguments.
X	 */
X	outfile = NULL;
X	while (--argc > 0 && **(++argv) == '-')
X	{
X		switch (argv[0][1])
X		{
X		case 'o':
X			outfile = &argv[0][2];
X			if (*outfile == '\0')
X			{
X				if (--argc <= 0)
X					usage();
X				outfile = *(++argv);
X			}
X			break;
X		default:
X			usage();
X		}
X	}
X	if (argc > 1)
X		usage();
X
X
X	/*
X	 * Open the input file, or use standard input if none specified.
X	 */
X	if (argc > 0)
X	{
X		if ((desc = fopen(*argv, "r")) == NULL)
X		{
X			perror(*argv);
X			exit(1);
X		}
X	} else
X		desc = stdin;
X
X	/*
X	 * Read the input file, one line at a time.
X	 * Each line consists of a command string,
X	 * followed by white space, followed by an action name.
X	 */
X	linenum = 0;
X	errors = 0;
X	up = usertable;
X	while (fgets(line, sizeof(line), desc) != NULL)
X	{
X		++linenum;
X
X		/*
X		 * Skip leading white space.
X		 * Replace the final newline with a null byte.
X		 * Ignore blank lines and comment lines.
X		 */
X		p = line;
X		while (*p == ' ' || *p == '\t')
X			++p;
X		for (i = 0;  p[i] != '\n' && p[i] != '\0';  i++)
X			;
X		p[i] = '\0';
X		if (*p == '#' || *p == '\0')
X			continue;
X
X		/*
X		 * Parse the command string and store it in the usertable.
X		 */
X		currcmd = up;
X		do
X		{
X			if (up >= usertable + MAX_USERCMD)
X			{
X				fprintf(stderr, "too many commands, line %d\n",
X					linenum);
X				exit(1);
X			}
X			if (up >= currcmd + MAX_CMDLEN)
X			{
X				fprintf(stderr, "command too long on line %d\n",
X					linenum);
X				errors++;
X				break;
X			}
X
X			*up++ = tchar(&p);
X
X		} while (*p != ' ' && *p != '\t' && *p != '\0');
X
X		/*
X		 * Terminate the command string with a null byte.
X		 */
X		*up++ = '\0';
X
X		/*
X		 * Skip white space between the command string
X		 * and the action name.
X		 * Terminate the action name if it is followed
X		 * by whitespace or a # comment.
X		 */
X		if (*p == '\0')
X		{
X			fprintf(stderr, "missing whitespace on line %d\n",
X				linenum);
X			errors++;
X			continue;
X		}
X		while (*p == ' ' || *p == '\t')
X			++p;
X		for (j = 0;  p[j] != ' ' && p[j] != '\t' && 
X			     p[j] != '#' && p[j] != '\0';  j++)
X			;
X		p[j] = '\0';
X
X		/*
X		 * Parse the action name and store it in the usertable.
X		 */
X		for (i = 0;  cmdnames[i].cn_name != NULL;  i++)
X			if (strcmp(cmdnames[i].cn_name, p) == 0)
X				break;
X		if (cmdnames[i].cn_name == NULL)
X		{
X			fprintf(stderr, "unknown action <%s> on line %d\n",
X				p, linenum);
X			errors++;
X			continue;
X		}
X		if (cmdnames[i].cn_action == A_TOGGLE_OPTION)
X		{
X			for (j = j+1;  p[j] == ' ' || p[j] == '\t';  j++)
X				;
X			if (p[j] != '\0')
X				*up++ = A_1_TOGGLE_OPTION | p[j];
X			else
X				*up++ = A_TOGGLE_OPTION;
X		} else
X			*up++ = cmdnames[i].cn_action;
X	}
X
X	if (errors > 0)
X	{
X		fprintf(stderr, "%d errors; no output produced\n", errors);
X		exit(1);
X	}
X
X	/*
X	 * Write the output file.
X	 * If no output file was specified, use "$HOME/.less"
X	 */
X	if (outfile == NULL)
X	{
X		p = getenv("HOME");
X		if (p == NULL)
X		{
X			fprintf(stderr, "cannot find $HOME\n");
X			exit(1);
X		}
X		strcpy(line, p);
X		strcat(line, "/.less");
X		outfile = line;
X	}
X	if ((out = fopen(outfile, "w")) == NULL)
X		perror(outfile);
X	else
X		fwrite((char *)usertable, 1, up-usertable, out);
X}
X
X/*
X * Parse one character of the command string.
X */
Xtchar(pp)
X	char **pp;
X{
X	register char *p;
X	register char ch;
X	register int i;
X
X	p = *pp;
X	switch (*p)
X	{
X	case '\\':
X		if (*++p >= '0' && *p <= '7')
X		{
X			/*
X			 * Parse an octal number.
X			 */
X			ch = 0;
X			i = 0;
X			do
X				ch = 8*ch + (*p - '0');
X			while (*++p >= '0' && *p <= '7' && ++i < 3);
X			*pp = p;
X			return (ch);
X		}
X		/*
X		 * Backslash followed by a char just means that char.
X		 */
X		*pp = p+1;
X		return (*p);
X	case '^':
X		/*
X		 * Carat means CONTROL.
X		 */
X		*pp = p+2;
X		return (CONTROL(p[1]));
X	}
X	*pp = p+1;
X	return (*p);
X}
X
Xusage()
X{
X	fprintf(stderr, "usage: lesskey [-o output] [input]\n");
X	exit(1);
X}
END_OF_FILE

mark@unix386.Convergent.COM (Mark Nudelman) (09/15/89)

#! /bin/sh
# This is a shell archive.
# Remove anything before this line, then unpack it
# by saving it into a file and typing "sh file".

echo shar: Extracting \"linstall\"
sed "s/^X//" >'linstall' <<'END_OF_FILE'
X#! /bin/sh
X# Installation script for less.
X# This script prompts the operator for various information
X# and constructs a makefile.
X
Xecho "This script will build a makefile for less."
Xecho "If you already have a file called \"makefile\" it will be overwritten."
Xecho "Press RETURN to continue."
Xread ans
X
Xecho "I will ask you some questions about your system."
Xecho "If you do not know the answer to any question,"
Xecho "just press RETURN and I will choose a default for you."
Xecho "Press RETURN now."
Xread ans
X
XECHO=./vecho
Xif [ ! -f $ECHO ]
Xthen
X	echo "One moment..."
X	cc -o $ECHO vecho.c
X	echo ""
Xfi
X
X$ECHO "Most Unix systems are derived from either System V"
X$ECHO "or Berkeley BSD 4.1, 4.2, 4.3, etc."
X$ECHO ""
X$ECHO "Is your system closest to:"
X$ECHO "  1. System V"
X$ECHO "  2. BSD 4.1"
X$ECHO "  3. BSD 4.2 or later"
X$ECHO "  4. Xenix"
X$ECHO "Enter a number, or just RETURN if you don't know: \c"
Xread ans
Xxenix=0
Xcase "X$ans" in
XX1) sys=sys5; sysname="System V" ;;
XX2) sys=bsd; bsd41=1; sysname="BSD 4.1" ;;
XX3) sys=bsd; bsd41=0; sysname="BSD 4.2" ;;
XX4) sys=sys5; xenix=1; sysname="Xenix" ;;
X*) sys=unknown ;;
Xesac
X$ECHO ""
X
XDATE=`date`
Xcat >makefile <<EOF
X# Makefile for "less"
X# Generated $DATE by $0.
XEOF
X
Xcat >>makefile <<"EOF"
X#
X# Invoked as:
X#	make all
X#   or	make install
X# Plain "make" is equivalent to "make all".
X#
X# If you add or delete functions, remake funcs.h by doing:
X#	make newfuncs
X# This depends on the coding convention of function headers looking like:
X#	" \t public <function-type> \n <function-name> ( ... ) "
X#
X# Also provided:
X#	make lint	# Runs "lint" on all the sources.
X#	make clean	# Removes "less" and the .o files.
X#	make clobber	# Pretty much the same as make "clean".
X
XSHELL = /bin/sh
X
XEOF
X
Xcat >defines.h <<EOF
X/* Definition file for less */
X/* Generated $DATE by $0. */
X
XEOF
X
Xcat >>defines.h <<EOF
X/*
X * Define XENIX if running under XENIX 3.0.
X */
X#define	XENIX		$xenix
X
XEOF
X$ECHO ""
X
X
X
Xif [ "X$sys" = "Xunknown" ]
Xthen
X	alldefault=0
Xelse
X	def=yes
X	alldefault=1
X	$ECHO "Do you want to use ALL the defaults for $sysname?"
X	$ECHO "  Enter \"yes\" if you have a STANDARD $sysname."
X	$ECHO "  Enter \"no\" if you want to change any of the defaults. [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) alldefault=1 ;;
X	X[nN]*) alldefault=0 ;;
X	esac
X	$ECHO ""
Xfi
X
Xif [ $alldefault = 0 ]
Xthen
X	alloptional=0
Xelse
X	def=yes
X	alloptional=1
X	$ECHO "Do you want to use all the optional features of less?"
X	$ECHO "  Less has several features which you may or may not"
X	$ECHO "  wish to include, such as shell escapes."
X	$ECHO "  Enter \"yes\" if you want to include ALL the optional features."
X	$ECHO "  Enter \"no\" if you want to select individual features. [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) alloptional=1 ;;
X	X[nN]*) alloptional=0 ;;
X	esac
X	$ECHO ""
Xfi
X
X
X
Xdef=yes
Xx=1
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Does your C compiler support the \"void\" type? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * VOID is 1 if your C compiler supports the "void" type,
X * 0 if it does not.
X */
X#define	VOID		$x
X
XEOF
X
X
X
Xdef=long
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "What type is the \"offset\" argument to lseek? [$def] \c"
X	read ans
X	if [ "X$ans" != "X" ]
X	then
X		def=$ans
X	fi
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * offset_t is the type which lseek() returns.
X * It is also the type of lseek()'s second argument.
X */
X#define	offset_t	$def
X
XEOF
X
X
X
X
Xdef=yes; x=1
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Most Unix systems provide the stat() function."
X	$ECHO "Does your system have stat()? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * STAT is 1 if your system has the stat() call.
X */
X#define	STAT		$x
X
XEOF
X
X
X
X
Xdef=yes; x=1
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Most Unix systems provide the perror() function."
X	$ECHO "Does your system have perror()? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * PERROR is 1 if your system has the perror() call.
X * (Actually, if it has sys_errlist, sys_nerr and errno.)
X */
X#define	PERROR		$x
X
XEOF
X
X
X
X
Xdef=yes; x=1
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Most Unix systems provide the time() function."
X	$ECHO "Does your system have time()? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * GET_TIME is 1 if your system has the time() call.
X */
X#define	GET_TIME	$x
X
XEOF
X
Xif [ $x = 0 ]
Xthen
X	$ECHO "What is the APPROXIMATE performance of your"
X	$ECHO "machine, as a percentage of a Vax 11/750?"
X	$ECHO "(Enter 100 if your machine is as fast as a Vax,"
X	$ECHO " 50 if it is half as fast, 200 if it is twice as fast, etc.)"
X	$ECHO "The accuracy of this information is not critical."
X	while :
X	do
X		$ECHO "Percent of Vax 11/750 [100]: \c"
X		read ans
X		if [ "X$ans" = "X" ]
X		then
X			ans=100
X		fi
X		longloop=`expr "$ans" "*" 3`
X		if [ $? = 0 ]
X		then
X			break
X		fi
X		$ECHO "Enter a number please!"
X	done
X	$ECHO ""
X
X	cat >>defines.h <<EOF
X/*
X * LONGLOOP is the number of lines we should process in the line number
X * scan before displaying a warning that it will take a while.
X */
X#define	LONGLOOP	($longloop)
XEOF
Xfi
X
X
X
X
Xif [ "$sys" = "bsd" ]
Xthen
X	def=no; x=0
Xelse
X	def=yes; x=1
Xfi
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Most System V systems have termio.h, while most"
X	$ECHO "Berkeley-derived systems have sgtty.h."
X	$ECHO "Does your system have termio.h? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * TERMIO is 1 if your system has /usr/include/termio.h.
X * This is normally the case for System 5.
X * If TERMIO is 0 your system must have /usr/include/sgtty.h.
X * This is normally the case for BSD.
X */
X#define	TERMIO		$x
X
XEOF
X
X
X
X
Xif [ "$sys" = "bsd" -a "$bsd41" = "0" ]
Xthen
X	def=yes; x=1
Xelse
X	def=no; x=0
Xfi
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Most BSD 4.2 and 4.3 systems have the sigsetmask() call."
X	$ECHO "Most System V and BSD 4.1 systems do not."
X	$ECHO "Does your system have sigsetmask()? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * SIGSETMASK is 1 if your system has the sigsetmask() call.
X * This is normally the case only for BSD 4.2,
X * not for BSD 4.1 or System 5.
X */
X#define	SIGSETMASK	$x
X
XEOF
X
X
X
Xif [ "$sys" = "bsd" ]
Xthen
X	def=2; REGCMP=0;RECOMP=1
Xelse
X	def=1; REGCMP=1;RECOMP=0
Xfi
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "Most System V systems have the regcmp() function."
X	$ECHO "Most Berkeley-derived systems have the re_comp() function."
X	$ECHO "Does your system have:"
X	$ECHO "  1. regcmp"
X	$ECHO "  2. re_comp"
X	$ECHO "  3. neither   [$def] \c"
X	read ans
X	case "X$ans" in
X	X1) REGCMP=1;RECOMP=0 ;;
X	X2) REGCMP=0;RECOMP=1 ;;
X	X3) REGCMP=0;RECOMP=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * REGCMP is 1 if your system has the regcmp() function.
X * This is normally the case for System 5.
X * RECOMP is 1 if your system has the re_comp() function.
X * This is normally the case for BSD.
X * If neither is 1, pattern matching is supported, but without metacharacters.
X */
X#define	REGCMP		$REGCMP
X#define	RECOMP		$RECOMP
X
XEOF
X
X
X
X
Xdef=yes
Xx=1
Xif [ $alloptional = 0 ]
Xthen
X	$ECHO "Do you wish to allow shell escapes? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * SHELL_ESCAPE is 1 if you wish to allow shell escapes.
X * (This is possible only if your system supplies the system() function.)
X */
X#define	SHELL_ESCAPE	$x
X
XEOF
X
X
X
Xdef=yes
Xx=1
Xedname="vi"
Xif [ $alloptional = 0 ]
Xthen
X	$ECHO "Do you wish to allow editor escapes? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[nN]*) x=0; edname="" ;;
X	X[yY]*) x=1
X		$ECHO "What is the pathname of the default editor? [$edname] \c"
X		read ans 
X		if [ "x$ans" != "x" ]
X		then
X			edname=$ans
X		fi
X		;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * EDITOR is 1 if you wish to allow editor invocation (the "v" command).
X * (This is possible only if your system supplies the system() function.)
X * EDIT_PGM is the name of the (default) editor to be invoked.
X */
X#define	EDITOR		$x
X#define	EDIT_PGM	"$edname"
X
XEOF
X
X
X
X
Xdef=yes
Xx=1
Xif [ $alloptional = 0 ]
Xthen
X	$ECHO "Do you wish to support \"tag\" files? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * TAGS is 1 if you wish to support tag files.
X */
X#define	TAGS		$x
X
XEOF
X
X
X
Xdef=yes
Xx=1
Xif [ $alloptional = 0 ]
Xthen
X	$ECHO "Do you wish to allow user-defined key definitions? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
XUSERFILE=$x
Xcat >>defines.h <<EOF
X/*
X * USERFILE is 1 if you wish to allow a .less file to specify 
X * user-defined key bindings.
X */
X#define	USERFILE	$x
X
XEOF
X
X
X
Xdef=yes
Xx=1
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "If your system provides the popen() function and"
X	$ECHO "the \"echo\" shell command, you may allow shell metacharacters" 
X	$ECHO "to be expanded in filenames."
X	$ECHO "Do you wish to allow shell metacharacters in filenames? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * GLOB is 1 if you wish to have shell metacharacters expanded in filenames.
X * This will generally work if your system provides the "popen" function
X * and the "$ECHO" shell command.
X */
X#define	GLOB		$x
X
XEOF
X
X
X
Xdef=yes
Xx=1
Xif [ $alloptional = 0 ]
Xthen
X	$ECHO "Do you wish to allow log files (-l option)? [$def] \c"
X	read ans
X	case "X$ans" in
X	X[yY]*) x=1 ;;
X	X[nN]*) x=0 ;;
X	esac
X	$ECHO ""
Xfi
Xcat >>defines.h <<EOF
X/*
X * LOGFILE is 1 if you wish to allow the -l option (to create log files).
X */
X#define	LOGFILE		$x
X
XEOF
X
Xcat >>defines.h <<EOF
X/*
X * ONLY_RETURN is 1 if you want RETURN to be the only input which
X * will continue past an error message.
X * Otherwise, any key will continue past an error message.
X */
X#define	ONLY_RETURN	0
X
XEOF
X
Xcat >>makefile <<EOF
X
X##########################################################################
X# Compilation environment.
X##########################################################################
X
XEOF
X
X
X
Xif [ "$xenix" = "1" ]
Xthen
X	LIBS="-ltermlib"
Xelif [ "$sys" = "bsd" ]
Xthen
X	LIBS="-ltermcap"
Xelse
X	LIBS="-lcurses -ltermcap -lPW"
Xfi
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "To build \"less\", you must link with libraries supplied by your system."
X	$ECHO "(If this needs to be changed later, edit the makefile"
X	$ECHO "and change the definition of LIBS.)"
X	$ECHO "What libraries should be used [$LIBS] \c"
X	read ans
X	if [ "X$ans" != "X" ]
X	then
X		LIBS="$ans"
X	fi
X	$ECHO ""
Xfi
Xcat >>makefile <<EOF
X# LIBS is the list of libraries needed.
XLIBS = $LIBS
X
XEOF
X
X
X
XINSTALL_LESS="/usr/local/bin/less"
XINSTALL_KEY="/usr/local/bin/lesskey"
XINSTALL_HELP="/usr/local/bin/less.help"
XINSTALL_LESSMAN="/usr/man/man1/less.1"
XINSTALL_KEYMAN="/usr/man/man1/lesskey.1"
XLESS_MANUAL="less.nro"
XKEY_MANUAL="lesskey.nro"
Xif [ $alldefault = 0 ]
Xthen
X	$ECHO "What is the name of the \"public\" (installed) version of less?"
X	$ECHO " [$INSTALL_LESS] \c"
X	read ans
X	if [ "X$ans" != "X" ]
X	then
X		INSTALL_LESS="$ans"
X	fi
X	$ECHO "What is the name of the \"public\" (installed) version of lesskey?"
X	$ECHO " [$INSTALL_KEY] \c"
X	read ans
X	if [ "X$ans" != "X" ]
X	then
X		INSTALL_KEY="$ans"
X	fi
X	$ECHO "What is the name of the \"public\" (installed) version of the help file?"
X	$ECHO " [$INSTALL_HELP] \c"
X	read ans
X	if [ "X$ans" != "X" ]
X	then
X		INSTALL_HELP="$ans"
X	fi
X	$ECHO "What is the name of the \"public\" (installed) version of the less manual page?"
X	$ECHO " [$INSTALL_LESSMAN] \c"
X	read ans
X	if [ "X$ans" != "X" ]
X	then
X		INSTALL_LESSMAN="$ans"
X	fi
X	$ECHO "What is the name of the \"public\" (installed) version of the lesskey manual page?"
X	$ECHO " [$INSTALL_KEYMAN] \c"
X	read ans
X	if [ "X$ans" != "X" ]
X	then
X		INSTALL_KEYMAN="$ans"
X	fi
X	$ECHO ""
Xfi
Xcat >>makefile <<EOF
X# INSTALL_LESS is a list of the public versions of less.
X# INSTALL_KEY is a list of the public versions of lesskey.
X# INSTALL_HELP is a list of the public version of the help file.
X# INSTALL_LESSMAN is a list of the public versions of the less manual page.
X# INSTALL_KEYMAN is a list of the public versions of the lesskey manual page.
XINSTALL_LESS =		\$(ROOT)$INSTALL_LESS
XINSTALL_KEY =		\$(ROOT)$INSTALL_KEY
XINSTALL_HELP =		\$(ROOT)$INSTALL_HELP
XINSTALL_LESSMAN =	\$(ROOT)$INSTALL_LESSMAN
XINSTALL_KEYMAN =	\$(ROOT)$INSTALL_KEYMAN
XLESS_MANUAL =		$LESS_MANUAL
XKEY_MANUAL =		$KEY_MANUAL
XHELPFILE =		$INSTALL_HELP
X
X
XEOF
X
X
X
Xcat >>makefile <<"EOF"
X# OPTIM is passed to the compiler and the loader.
X# It is normally "-O" but may be, for example, "-g".
XOPTIM = -O
X
XCFLAGS = $(OPTIM)
X
X
X
X##########################################################################
X# Files
X##########################################################################
X
XSRC1 =	ch.c command.c decode.c help.c input.c line.c 
XSRC2 =	linenum.c main.c option.c optfunc.c opttbl.c os.c output.c position.c handle.c
XSRC3 =	prim1.c prim2.c prompt.c screen.c signal.c tags.c ttyin.c version.c
X
XSRC =	$(SRC1) $(SRC2) $(SRC3)
X
XOBJ =	ch.o command.o decode.o help.o input.o line.o linenum.o main.o \
X	option.o optfunc.o opttbl.o os.o output.o position.o prim1.o prim2.o \
X	handle.o prompt.o screen.o signal.o tags.o ttyin.o version.o
X
X
X##########################################################################
X# Rules for building stuff
X##########################################################################
X
XEOF
X
Xif [ "$USERFILE" = "1" ]
Xthen
X	cat >>makefile <<"EOF"
Xall: less lesskey
Xinstall: install_less install_help install_key install_lman install_kman
XEOF
Xelse
X	cat >>makefile <<"EOF"
Xall: less
Xinstall: install_less install_help install_lman
XEOF
Xfi
X
Xcat >>makefile <<"EOF"
X
Xless: $(OBJ)
X	$(CC) $(LDFLAGS) $(OPTIM) -o less $(OBJ) $(LIBS) $(LDLIBS)
X
Xlesskey: lesskey.o
X	$(CC) $(LDFLAGS) $(OPTIM) -o lesskey lesskey.o $(LDLIBS)
X
X# help.o depends on makefile for the definition of HELPFILE
Xhelp.o: makefile
X	$(CC) $(CFLAGS) -c -DHELPFILE=\"$(HELPFILE)\" help.c
X
Xinstall_less: less
X	for f in $(INSTALL_LESS); do  rm -f $$f; cp less $$f;  done
X	touch install_less
X
Xinstall_key: lesskey
X	for f in $(INSTALL_KEY); do  rm -f $$f; cp lesskey $$f;  done
X	touch install_key
X
Xinstall_help: less.help
X	for f in $(INSTALL_HELP); do  rm -f $$f; cp less.help $$f;  done
X	touch install_help
X
Xinstall_lman: $(LESS_MANUAL) 
X	for f in $(INSTALL_LESSMAN); do  rm -f $$f; cp $(LESS_MANUAL) $$f;  done
X	touch install_lman
X
Xinstall_kman: $(KEY_MANUAL)
X	for f in $(INSTALL_KEYMAN); do  rm -f $$f; cp $(KEY_MANUAL) $$f;  done
X	touch install_kman
X
X##########################################################################
X# Maintenance
X##########################################################################
X
Xlint:
X	lint -hp $(SRC)
X
Xnewfuncs funcs.h:
X	if [ -f funcs.h ]; then mv funcs.h funcs.h.OLD; fi
X	awk -f mkfuncs.awk $(SRC) >funcs.h
X
Xclean:
X	rm -f $(OBJ) lesskey.o less lesskey vecho
X
Xclobber:
X	rm -f *.o less lesskey vecho install_less install_key \
X		install_help install_lman install_kman
X
Xshar:
X	shar -v README CHANGES less.nro lesskey.nro \
X		vecho.c mkfuncs.awk less.help \
X		less.h position.h funcs.h cmd.h option.h \
X		lesskey.c > less1.shr
X	shar -v linstall less.man lesskey.man > less2.shr 
X	shar -v $(SRC1) > less3.shr
X	shar -v $(SRC2) > less4.shr
X	shar -v $(SRC3) > less5.shr
X
X
X##########################################################################
X# Dependencies
X##########################################################################
X
X$(OBJ): less.h funcs.h defines.h position.h
Xcommand.o decode.o: cmd.h
Xoption.o opttbl.o optfunc.o: option.h
X
Xlesskey.o: less.h funcs.h defines.h cmd.h
X
XEOF
X$ECHO ""
X
X$ECHO "The makefile has been built."
X$ECHO "You should check it to make sure everything is as you want it to be."
X$ECHO "When you are satisfied with the makefile, just type \"make\""
X$ECHO "and \"less\" will be built."
END_OF_FILE
echo shar: Extracting \"less.man\"
sed "s/^X//" >'less.man' <<'END_OF_FILE'
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X     NNNNAAAAMMMMEEEE
X          less - opposite of more
X
X     SSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X          lllleeeessssssss ----????
X          lllleeeessssssss [[[[----[[[[++++]]]]aaaaAAAABBBBccccCCCCddddeeeeEEEEffffggggiiiimmmmMMMMnnnnNNNNqqqqQQQQrrrruuuuUUUUsssswwww]]]] [[[[----bbbb_N]]]] [[[[----hhhh_N]]]] [[[[----xxxx_N]]]] [[[[----[[[[zzzz]]]]_N]]]]
X               [[[[----PPPP[[[[mmmmMMMM====]]]]_s_t_r_i_n_g]]]] [[[[----[[[[llllLLLL]]]]_l_o_g_f_i_l_e]]]] [[[[----kkkk_k_e_y_f_i_l_e]]]]
X               [[[[++++_c_m_d]]]] [[[[----tttt_t_a_g]]]] [[[[_f_i_l_e_n_a_m_e]]]]............
X
X
X     DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN
X          _L_e_s_s is a program similar to _m_o_r_e (1), but which allows
X          backwards movement in the file as well as forward movement.
X          Also, _l_e_s_s does not have to read the entire input file
X          before starting, so with large input files it starts up
X          faster than text editors like _v_i (1).  _L_e_s_s uses termcap (or
X          terminfo on some systems), so it can run on a variety of
X          terminals.  There is even limited support for hardcopy
X          terminals.  (On a hardcopy terminal, lines which should be
X          printed at the top of the screen are prefixed with an up-
X          arrow.)
X
X          Commands are based on both _m_o_r_e and _v_i. Commands may be
X          preceded by a decimal number, called N in the descriptions
X          below.  The number is used by some commands, as indicated.
X
X
X     CCCCOOOOMMMMMMMMAAAANNNNDDDDSSSS
X          In the following descriptions, ^X means control-X.  ESC
X          stands for the ESCAPE key; for example ESC-v means the two
X          character sequence "ESCAPE", then "v".
X
X          h or H
X               Help: display a summary of these commands.  If you
X               forget all the other commands, remember this one.
X
X          SPACE or ^V or f or ^F
X               Scroll forward N lines, default one window (see option
X               -z below).  If N is more than the screen size, only the
X               final screenful is displayed.  Warning: some systems
X               use ^V as a special literalization character.
X
X          z    Like SPACE, but if N is specified, it becomes the new
X               window size.
X
X          RETURN or ^N or e or ^E or j or ^J
X               Scroll forward N lines, default 1.  The entire N lines
X               are displayed, even if N is more than the screen size.
X
X          d or ^D
X               Scroll forward N lines, default one half of the screen
X               size.  If N is specified, it becomes the new default
X
X
X
X     Page 1                                          (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               for subsequent d and u commands.
X
X          b or ^B or ESC-v
X               Scroll backward N lines, default one window (see option
X               -z below).  If N is more than the screen size, only the
X               final screenful is displayed.
X
X          w    Like ESC-v, but if N is specified, it becomes the new
X               window size.
X
X          y or ^Y or ^P or k or ^K
X               Scroll backward N lines, default 1.  The entire N lines
X               are displayed, even if N is more than the screen size.
X               Warning: some systems use ^Y as a special job control
X               character.
X
X          u or ^U
X               Scroll backward N lines, default one half of the screen
X               size.  If N is specified, it becomes the new default
X               for subsequent d and u commands.
X
X          r or ^R or ^L
X               Repaint the screen.
X
X          R    Repaint the screen, discarding any buffered input.
X               Useful if the file is changing while it is being
X               viewed.
X
X          g or < or ESC-<
X               Go to line N in the file, default 1 (beginning of
X               file).  (Warning: this may be slow if N is large.)
X
X          G or > or ESC->
X               Go to line N in the file, default the end of the file.
X               (Warning: this may be slow if N is large, or if N is
X               not specified and standard input, rather than a file,
X               is being read.)
X
X          p    Go to a position N percent into the file.  N should be
X               between 0 and 100.  (This works if standard input is
X               being read, but only if _l_e_s_s has already read to the
X               end of the file.  It is always fast, but not always
X               useful.)
X
X          %    If a bracket appears in the top line displayed on the
X               screen, the % command will go to the matching bracket.
X               A "bracket" is one of the characters "{ } [ ] ( )".
X
X          {    If a left curly bracket appears in the top line
X               displayed on the screen, the { command will go to the
X               matching right curly bracket.  If there is more than
X               one left curly bracket on the top line, a number N may
X
X
X
X     Page 2                                          (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               be used to specify the N-th bracket on the line.
X
X          }    If a right curly bracket appears in the top line
X               displayed on the screen, the } command will go to the
X               matching left curly bracket.  If there is more than one
X               right curly bracket on the top line, a number N may be
X               used to specify the N-th bracket on the line.
X
X          (    Like {, but applies to parentheses rather than curly
X               brackets.
X
X          )    Like }, but applies to parentheses rather than curly
X               brackets.
X
X          [    Like {, but applies to square brackets rather than
X               curly brackets.
X
X          ]    Like }, but applies to square brackets rather than
X               curly brackets.
X
X          m    Followed by any lowercase letter, marks the current
X               position with that letter.
X
X          '    (Single quote.) Followed by any lowercase letter,
X               returns to the position which was previously marked
X               with that letter.  Followed by another single quote,
X               returns to the position at which the last "large"
X               movement command was executed.  Marks are preserved
X               when a new file is examined, so the ' command can be
X               used to switch between input files.
X
X          ^X^X Same as single quote.
X
X          /pattern
X               Search forward in the file for the N-th line containing
X               the pattern.  N defaults to 1.  The pattern is a
X               regular expression, as recognized by _e_d. The search
X               starts at the second line displayed (but see the -a
X               option, which changes this).
X
X          ?pattern
X               Search backward in the file for the N-th line
X               containing the pattern.  The search starts at the line
X               immediately before the top line displayed.
X
X          ESC-/pattern
X               Search the entire file for the N-th line containing the
X               pattern.  The search starts at the first line in the
X               file, regardless of what is displayed or the setting of
X               the -a option.
X
X          /!pattern
X
X
X
X     Page 3                                          (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               Like /, but the search is for the N-th line which does
X               NOT contain the pattern.
X
X          ?!pattern
X               Like ?, but the search is for the N-th line which does
X               NOT contain the pattern.
X
X          ESC-/!pattern
X               Like ESC-/, but the search is for the N-th line which
X               does NOT contain the pattern.
X
X          n    Repeat previous search, for N-th line containing the
X               last pattern (or NOT containing the last pattern, if
X               the previous search was /! or ?!).
X
X          ESC-n
X               Repeat previous search, but in the reverse direction.
X
X          E [filename]
X               Examine a new file.  If the filename is missing, the
X               "current" file (see the N and P commands below) from
X               the list of files in the command line is re-examined.
X               A percent sign (%) in the filename is replaced by the
X               name of the current file. A pound sign (#) is replaced
X               by the name of the previously examined file.
X
X          ^X^V or :e
X               Same as E.  Warning: some systems use ^V as a special
X               literalization character.
X
X          N or :n
X               Examine the next file (from the list of files given in
X               the command line).  If a number N is specified (not to
X               be confused with the command N), the N-th next file is
X               examined.
X
X          P or :p
X               Examine the previous file.  If a number N is specified,
X               the N-th previous file is examined.
X
X          = or ^G or :f
X               Prints some information about the file being viewed,
X               including its name and the line number and byte offset
X               of the bottom line being displayed.  If possible, it
X               also prints the length of the file, the number of lines
X               in the file and the percent of the file above the last
X               displayed line.
X
X          -    Followed by one of the command line option letters (see
X               below), this will change the setting of that option and
X               print a message describing the new setting.  If the
X               option letter has a numeric value (such as -b or -h),
X
X
X
X     Page 4                                          (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               or a string value (such as -P or -t), a new value may
X               be entered after the option letter.
X
X          _    (Underscore.) Followed by one of the command line
X               option letters (see below), this will print a message
X               describing the current setting of that option.  The
X               setting of the option is not changed.
X
X          +cmd Causes the specified cmd to be executed each time a new
X               file is examined.  For example, +G causes _l_e_s_s to
X               initially display each file starting at the end rather
X               than the beginning.
X
X          V    Prints the version number of _l_e_s_s being run.
X
X          q or :q or :Q or ZZ
X               Exits _l_e_s_s.
X
X          The following two commands may or may not be valid,
X          depending on your particular installation.
X
X          v    Invokes an editor to edit the current file being
X               viewed.  The editor is taken from the environment
X               variable EDITOR, or defaults to "vi".  See also the
X               discussion of EDITPROTO under the section on PROMPTS
X               below.
X
X          ! shell-command
X               Invokes a shell to run the shell-command given.  A
X               percent sign (%) in the command is replaced by the name
X               of the current file. A pound sign (#) is replaced by
X               the name of the previously examined file.  "!!" repeats
X               the last shell command.  "!" with no shell command
X               simply invokes a shell.  In all cases, the shell is
X               taken from the environment variable SHELL, or defaults
X               to "sh".
X
X     OOOOPPPPTTTTIIIIOOOONNNNSSSS
X          Command line options are described below.  Most options may
X          be changed while _l_e_s_s is running, via the "-" command.
X
X          Options are also taken from the environment variable "LESS".
X          For example, to avoid typing "less -options ..." each time
X          _l_e_s_s is invoked, you might tell _c_s_h:
X
X          setenv LESS "-options"
X
X          or if you use _s_h:
X
X          LESS="-options"; export LESS
X
X          The environment variable is parsed before the command line,
X
X
X
X     Page 5                                          (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X          so command line options override the LESS environment
X          variable.  If an option appears in the LESS variable, it can
X          be reset to its default on the command line by beginning the
X          command line option with "-+".
X
X          A dollar sign ($) may be used to signal the end of an option
X          string.  This is important only for options like -P which
X          take a following string.
X
X          -?   This option displays a summary of the commands accepted
X               by _l_e_s_s (the same as the h command).  If this option is
X               given, all other options are ignored, and _l_e_s_s exits
X               after the help screen is viewed.  (Depending on how
X               your shell interprets the question mark, it may be
X               necessary to quote the question mark, thus: "-\?".)
X
X          -a   Normally, forward searches start just after the top
X               displayed line (that is, at the second displayed line).
X               Thus, forward searches include the currently displayed
X               screen.  The -a option causes forward searches to start
X               just after the bottom line displayed, thus skipping the
X               currently displayed screen.
X
X          -A   The -A option causes searches to start at the second
X               SCREEN line displayed, as opposed to the default which
X               is to start at the second REAL line displayed.  For
X               example, suppose a long real line occupies the first
X               three screen lines.  The default search will start at
X               the second real line (the fourth screen line), while
X               the -A option will cause the search to start at the
X               second screen line (in the midst of the first real
X               line).  (This option is rarely useful.)
X
X          -b   The -b_n option tells _l_e_s_s to use a non-standard number
X               of buffers.  Buffers are 1K, and normally 10 buffers
X               are used (except if data in coming from standard input;
X               see the -B option).  The number _n specifies a different
X               number of buffers to use.
X
X          -B   Normally, when data is coming from standard input,
X               buffers are allocated automatically as needed, to avoid
X               loss of data.  The -B option disables this feature, so
X               that only the default number of buffers are used.  If
X               more data is read than will fit in the buffers, the
X               oldest data is discarded.
X
X          -c   Normally, _l_e_s_s will repaint the screen by scrolling
X               from the bottom of the screen.  If the -c option is
X               set, when _l_e_s_s needs to change the entire display, it
X               will paint from the top line down.
X
X          -C   The -C option is like -c, but the screen is cleared
X
X
X
X     Page 6                                          (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               before it is repainted.
X
X          -d   Normally, _l_e_s_s will complain if the terminal is dumb;
X               that is, lacks some important capability, such as the
X               ability to clear the screen or scroll backwards.  The
X               -d option suppresses this complaint (but does not
X               otherwise change the behavior of the program on a dumb
X               terminal).
X
X          -e   Normally the only way to exit _l_e_s_s is via the "q"
X               command.  The -e option tells _l_e_s_s to automatically
X               exit the second time it reaches end-of-file.
X
X          -E   The -E flag causes _l_e_s_s to exit the first time it
X               reaches end-of-file.
X
X          -f   Normally, _l_e_s_s will refuse to open a non-regular file
X               (that is, a file which is a directory or a device
X               special file).  The -f flag forces _l_e_s_s to open such
X               files.
X
X          -g   Force input characters to 7 bits (that is, mask off the
X               high order bit).  The default is to use full 8 bit
X               characters.
X
X          -h   Normally, _l_e_s_s will scroll backwards when backwards
X               movement is necessary.  The -h option specifies a
X               maximum number of lines to scroll backwards.  If it is
X               necessary to move backwards more than this many lines,
X               the screen is repainted in a forward direction.  (If
X               the terminal does not have the ability to scroll
X               backwards, -h0 is implied.)
X
X          -i   The -i option causes searches to ignore case; that is,
X               uppercase and lowercase are considered identical.
X               Also, text which is overstruck or underlined can be
X               searched for.
X
X          -k   The -k option, followed immediately by a filename, will
X               cause _l_e_s_s to open and interpret the file as a _l_e_s_s_k_e_y
X               (1) file.  Multiple -k options may be specified.  If a
X               file called .less exists in the user's home directory,
X               this file is also used as a _l_e_s_s_k_e_y file.
X
X          -l   The -l option, followed immediately by a filename, will
X               cause _l_e_s_s to copy its input to the named file as it is
X               being viewed.  This applies only when the input file is
X               a pipe, not an ordinary file.  If the file already
X               exists, _l_e_s_s will ask for confirmation before
X               overwriting it.
X
X          -L   The -L option is like -l, but it will overwrite an
X
X
X
X     Page 7                                          (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               existing file without asking for confirmation.
X
X               If no log file has been specified, the -l and -L
X               options can be used from within _l_e_s_s to specify a log
X               file.  Without a file name, they will simply report the
X               name of the log file.  The "s" command is equivalent to
X               specifying -l from within _l_e_s_s.
X
X          -m   Normally, _l_e_s_s prompts with a colon.  The -m option
X               causes _l_e_s_s to prompt verbosely (like _m_o_r_e), with the
X               percent into the file.
X
X          -M   The -M option causes _l_e_s_s to prompt even more verbosely
X               than _m_o_r_e.
X
X          -n   The -n flag suppresses line numbers.  The default (to
X               use line numbers) may cause _l_e_s_s to run more slowly in
X               some cases, especially with a very large input file.
X               Suppressing line numbers with the -n flag will avoid
X               this problem.  Using line numbers means: the line
X               number will be displayed in the verbose prompt and in
X               the = command, and the v command will pass the current
X               line number to the editor (see also the discussion of
X               EDITPROTO in PROMPTS below).
X
X          -N   The -N flag causes a line number to be displayed at the
X               beginning of each line in the display.
X
X          -P   The -P option provides a way to tailor the three prompt
X               styles to your own preference.  You would normally put
X               this option in your LESS environment variable, rather
X               than type it in with each _l_e_s_s command.  Such an option
X               must either be the last option in the LESS variable, or
X               be terminated by a dollar sign.  -P followed by a
X               string changes the default (short) prompt to that
X               string.  -Pm changes the medium (-m) prompt to the
X               string, and -PM changes the long (-M) prompt.  Also,
X               -P= changes the message printed by the = command to the
X               given string.  All prompt strings consist of a sequence
X               of letters and special escape sequences.  See the
X               section on PROMPTS for more details.
X
X          -q   Normally, if an attempt is made to scroll past the end
X               of the file or before the beginning of the file, the
X               terminal bell is rung to indicate this fact.  The -q
X               option tells _l_e_s_s not to ring the bell at such times.
X               If the terminal has a "visual bell", it is used
X               instead.
X
X          -Q   Even if -q is given, _l_e_s_s will ring the bell on certain
X               other errors, such as typing an invalid character.  The
X               -Q option tells _l_e_s_s to be quiet all the time; that is,
X
X
X
X     Page 8                                          (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               never ring the terminal bell.  If the terminal has a
X               "visual bell", it is used instead.
X
X          -r   Normally, control character are displayed using a carat
X               and the equivalent non-control character.  For example,
X               a control-A (octal 001) is displayed as "^A".  If the
X               -r flag is set, "raw" control characters are displayed,
X               without this translation.  Warning: when this flag is
X               used, _l_e_s_s cannot keep track of the actual appearance
X               of the screen (since this depends on how the screen
X               responds to each type of control character).  Thus,
X               various display problems may result, such as long lines
X               being split in the wrong place.
X
X          -s   The -s option causes consecutive blank lines to be
X               squeezed into a single blank line.  This is useful when
X               viewing _n_r_o_f_f output.
X
X          -t   The -t option, followed immediately by a TAG, will edit
X               the file containing that tag.  For this to work, there
X               must be a file called "tags" in the current directory,
X               which was previously built by the _c_t_a_g_s (1) command.
X               This option may also be specified from within _l_e_s_s
X               (using the - command) as a way of examining a new file.
X               For _v_i compatibility, the command ":ta" is equivalent
X               to specifying -t from within _l_e_s_s.
X
X          -u   If the -u option is given, backspaces are treated as
X               printable characters; that is, they are sent to the
X               terminal when they appear in the input.
X
X          -U   If the -U option is given, backspaces are printed as
X               the two character sequence "^H".
X
X               If neither -u nor -U is given, backspaces which appear
X               adjacent to an underscore character are treated
X               specially: the underlined text is displayed using the
X               terminal's hardware underlining capability.  Also,
X               backspaces which appear between two identical
X               characters are treated specially: the overstruck text
X               is printed using the terminal's hardware boldface
X               capability.  Other backspaces are deleted, along with
X               the preceding character.
X
X          -w   Normally, _l_e_s_s uses a tilde character to represent
X               lines past the end of the file.  The -w option causes
X               blank lines to be used instead.
X
X          -x   The -x_n option sets tab stops every _n positions.  The
X               default for _n is 8.
X
X          -[z] When given a backwards or forwards window command, _l_e_s_s
X
X
X
X     Page 9                                          (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               will by default scroll backwards or forwards one
X               screenful of lines. The -z_n option changes the default
X               scrolling window size to _n lines.  The z and w commands
X               can also be used to change the window size.  Note that
X               the "z" in "-z_n" is optional for compatibility with
X               _m_o_r_e.
X
X          +    If a command line option begins with ++++, the remainder
X               of that option is taken to be an initial command to
X               _l_e_s_s. For example, +G tells _l_e_s_s to start at the end of
X               the file rather than the beginning, and +/xyz tells it
X               to start at the first occurrence of "xyz" in the file.
X               As a special case, +<number> acts like +<number>g; that
X               is, it starts the display at the specified line number
X               (however, see the caveat under the "g" command above).
X               If the option starts with ++++++++, the initial command
X               applies to every file being viewed, not just the first
X               one.  The + command described previously may also be
X               used to set (or change) an initial command for every
X               file.
X
X
X     KKKKEEEEYYYY BBBBIIIINNNNDDDDIIIINNNNGGGGSSSS
X          You may define your own _l_e_s_s commands by using the program
X          _l_e_s_s_k_e_y (1) to create a file called ".less" in your home
X          directory.  This file specifies a set of command keys and an
X          action associated with each key.  See the _l_e_s_s_k_e_y manual
X          page for more details.
X
X
X     PPPPRRRROOOOMMMMPPPPTTTTSSSS
X          The -P option allows you to tailor the prompt to your
X          preference.  The string given to the -P option replaces the
X          specified prompt string.  Certain characters in the string
X          are interpreted specially.  The prompt mechanism is rather
X          complicated to provide flexibility, but the ordinary user
X          need not understand the details of constructing personalized
X          prompt strings.
X
X          A percent sign followed by a single character is expanded
X          according to what the following character is:
X
X          %bX  Replaced by the byte offset into the current input
X               file.  The b is followed by a single character (shown
X               as X above) which specifies the line whose byte offset
X               is to be used.  If the character is a "t", the byte
X               offset of the top line in the display is used, an "m"
X               means use the middle line, a "b" means use the bottom
X               line, and a "B" means use the line just after the
X               bottom line.
X
X          %E   Replaced by the name of the editor (from the EDITOR
X
X
X
X     Page 10                                         (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X               environment variable).  See the discussion of the
X               EDITPROTO feature below.
X
X          %f   Replaced by the name of the current input file.
X
X          %i   Replaced by the index of the current file in the list
X               of input files.
X
X          %lX  Replaced by the line number of a line in the input
X               file.  The line to be used is determined by the X, as
X               with the %b option.
X
X          %L   Replaced by the line number of the last line in the
X               input file.
X
X          %m   Replaced by the total number of input files.
X
X          %pX  Replaced by the percent into the current input file.
X               The line used is determined by the X as with the %b
X               option.
X
X          %s   Replaced by the size of the current input file.
X
X          %t   Causes any trailing spaces to be removed.  Usually used
X               at the end of the string, but may appear anywhere.
X
X          %x   Replaced by the name of the next input file in the
X               list.
X
X          If any item is unknown (for example, the file size if input
X          is a pipe), a question mark is printed instead.
X
X          The format of the prompt string can be changed depending on
X          certain conditions.  A question mark followed by a single
X          character acts like an "IF": depending on the following
X          character, a condition is evaluated.  If the condition is
X          true, any characters following the question mark and
X          condition character, up to a period, are included in the
X          prompt.  If the condition is false, such characters are not
X          included.  A colon appearing between the question mark and
X          the period can be used to establish an "ELSE": any
X          characters between the colon and the period are included in
X          the string if and only if the IF condition is false.
X          Condition characters (which follow a question mark) may be:
X
X          ?a   True if any characters have been included in the prompt
X               so far.
X
X          ?bX  True if the byte offset of the specified line is known.
X
X          ?e   True if at end-of-file.
X
X
X
X
X     Page 11                                         (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X          ?f   True if there is an input filename (that is, if input
X               is not a pipe).
X
X          ?lX  True if the line number of the specified line is known.
X
X          ?L   True if the line number of the last line in the file is
X               known.
X
X          ?m   True if there is more than one input file.
X
X          ?n   True if this is the first prompt in a new input file.
X
X          ?pX  True if the percent into the current input file of the
X               specified line is known.
X
X          ?s   True if the size of current input file is known.
X
X          ?x   True if there is a next input file (that is, if the
X               current input file is not the last one).
X
X          Any characters other than the special ones (question mark,
X          colon, period, percent, and backslash) become literally part
X          of the prompt.  Any of the special characters may be
X          included in the prompt literally by preceding it with a
X          backslash.
X
X          Some examples:
X
X          ?f%f:Standard input.
X
X          This prompt prints the filename, if known; otherwise the
X          string "Standard input".
X
X          ?f%f .?ltLine %lt:?pt%pt\%:?btByte %bt:-...
X
X          This prompt would print the filename, if known.  The
X          filename is followed by the line number, if known, otherwise
X          the percent if known, otherwise the byte offset if known.
X          Otherwise, a dash is printed.  Notice how each question mark
X          has a matching period, and how the % after the %pt is
X          included literally by escaping it with a backslash.
X
X          ?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\: %x..%t
X
X          This prints the filename if this is the first prompt in a
X          file, followed by the "file N of N" message if there is more
X          than one input file.  Then, if we are at end-of-file, the
X          string "(END)" is printed followed by the name of the next
X          file, if there is one.  Finally, any trailing spaces are
X          truncated.  This is the default prompt.  For reference, here
X          are the defaults for the other two prompts (-m and -M
X          respectively).  Each is broken into two lines here for
X
X
X
X     Page 12                                         (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X          readability only.
X
X          ?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\: %x.:
X               ?pB%pB\%:byte %bB?s/%s...%t
X
X          ?f%f .?n?m(file %i of %m) ..?ltline %lt?L/%L. :byte %bB?s/%s. .
X               ?e(END) ?x- Next\: %x.:?pB%pB\%..%t
X
X          And here is the default message produced by the = command:
X
X          ?f%f .?m(file %i of %m) .?ltline %lt?L/%L. .
X               byte %bB?s/%s. ?e(END) :?pB%pB\%..%t
X
X          The prompt expansion features are also used for another
X          purpose: if an environment variable EDITPROTO is defined, it
X          is used as the command to be executed when the v command is
X          invoked.  The EDITPROTO string is expanded in the same way
X          as the prompt strings.  The default value for EDITPROTO is:
X               %E ?lm+%lm. %f
X          Note that this expands to the editor name, followed by a +
X          and the line number, followed by the file name.  If your
X          editor does not accept the "+linenumber" syntax, or has
X          other differences in invocation syntax, the EDITPROTO
X          variable can be changed to modify this default.
X
X
X     EEEENNNNVVVVIIIIRRRROOOONNNNMMMMEEEENNNNTTTT VVVVAAAARRRRIIIIAAAABBBBLLLLEEEESSSS
X          EDITOR
X               The name of the editor (used for the v command).
X
X          EDITPROTO
X               Editor prototype string (used for the v command).  See
X               discussion under PROMPTS.
X
X          HOME Name of the user's home directory (used to find a .less
X               file).
X
X          LESS Flags which are passed to _l_e_s_s automatically.
X
X          SHELL
X               The shell used to execute the ! command, as well as to
X               expand filenames.
X
X          TERM The type of terminal on which _l_e_s_s is being run.
X
X
X     SSSSEEEEEEEE AAAALLLLSSSSOOOO
X          lesskey(1)
X
X
X     WWWWAAAARRRRNNNNIIIINNNNGGGGSSSS
X          The = command and prompts (unless changed by -P) report the
X
X
X
X     Page 13                                         (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSS((((1111))))                     UUUUNNNNIIIIXXXX 5555....0000                      LLLLEEEESSSSSSSS((((1111))))
X
X
X
X          line number of the line at the top of the screen, but the
X          byte and percent of the line at the bottom of the screen.
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X     Page 14                                         (printed 9/14/89)
X
X
X
END_OF_FILE
echo shar: Extracting \"lesskey.man\"
sed "s/^X//" >'lesskey.man' <<'END_OF_FILE'
X
X
X
X     LLLLEEEESSSSSSSSKKKKEEEEYYYY((((1111))))                  UUUUNNNNIIIIXXXX 5555....0000                   LLLLEEEESSSSSSSSKKKKEEEEYYYY((((1111))))
X
X
X
X     NNNNAAAAMMMMEEEE
X          lesskey - specify key bindings for less
X
X     SSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
X          lllleeeesssssssskkkkeeeeyyyy [[[[----oooo oooouuuuttttppppuuuutttt]]]] [[[[iiiinnnnppppuuuutttt]]]]
X
X     DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN
X          _L_e_s_s_k_e_y is used to specify a set of key bindings to be used
X          by _l_e_s_s. The input file is a text file which describes the
X          key bindings, and the output file is a binary file which is
X          used by _l_e_s_s. If no input file is specified, standard input
X          is used.  If no output file is specified, $HOME/.less is
X          used.
X
X          The input file consists of lines of the form:
X
X               string <whitespace> action <newline>
X
X          Whitespace is any sequence of one or more spaces and/or
X          tabs.  The "string" is the command key(s) which invoke the
X          action.  The string may be a single command key, or a
X          sequence of up to 15 keys.  The "action" is the name of the
X          less action, from the list below.  The characters in the
X          "string" may appear literally, or be prefixed by a carat to
X          indicate a control key.  A backslash may be used to cause
X          the following character to be taken literally.  Characters
X          which must be preceded by backslash include carat, space,
X          tab and the backslash itself.  A backslash followed by one
X          to three octal digits may be used to specify a character by
X          its octal value.  Blank lines and lines which start with a
X          pound sign (#) are ignored.
X
X          A special case is the "toggle-option" action.  This action
X          may be followed by a single letter indicating the option to
X          be toggled.  See the ":ta" command in the example below.
X
X          As an example, the following input file describes the set of
X          default command keys used by less:
X
X               k              back-line
X               y              back-line
X               ^K             back-line
X               ^Y             back-line
X               ^P             back-line
X               b              back-screen
X               ^B             back-screen
X               \33v           back-screen
X               u              back-scroll
X               ^U             back-scroll
X               ?              back-search
X               w              back-window
X               {              bracket-{
X
X
X
X     Page 1                                          (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSSKKKKEEEEYYYY((((1111))))                  UUUUNNNNIIIIXXXX 5555....0000                   LLLLEEEESSSSSSSSKKKKEEEEYYYY((((1111))))
X
X
X
X               }              bracket-}
X               (              bracket-(
X               )              bracket-)
X               [              bracket-[
X               ]              bracket-]
X               %              bracket-any
X               E              examine
X               :e             examine
X               ^X^V           examine
X               \33/           file-search
X               +              first-cmd
X               e              forw-line
X               j              forw-line
X               ^E             forw-line
X               ^J             forw-line
X               ^M             forw-line
X               ^N             forw-line
X               f              forw-screen
X               ^F             forw-screen
X               \40            forw-screen
X               ^V             forw-screen
X               d              forw-scroll
X               ^D             forw-scroll
X               /              forw-search
X               z              forw-window
X               G              goto-end
X               >              goto-end
X               \33>           goto-end
X               g              goto-line
X               <              goto-line
X               \33<           goto-line
X               '              goto-mark
X               ^X^X           goto-mark
X               h              help
X               H              help
X               N              next-file
X               :n             next-file
X               p              percent
X               P              prev-file
X               :p             prev-file
X               q              quit
X               :q             quit
X               :Q             quit
X               ZZ             quit
X               ^L             repaint
X               ^R             repaint
X               r              repaint
X               R              flush-repaint
X               n              repeat-search
X               \33n           reverse-search
X               m              set-mark
X               !              shell
X
X
X
X     Page 2                                          (printed 9/14/89)
X
X
X
X
X
X
X     LLLLEEEESSSSSSSSKKKKEEEEYYYY((((1111))))                  UUUUNNNNIIIIXXXX 5555....0000                   LLLLEEEESSSSSSSSKKKKEEEEYYYY((((1111))))
X
X
X
X               =              status
X               ^G             status
X               :f             status
X               -              toggle-option
X               :ta            toggle-option t
X               s              toggle-option l
X               _              display-option
X               V              version
X               v              visual
X
X
X          Commands specified by _l_e_s_s_k_e_y take precedence over the
X          default commands.  A default command key may be disabled by
X          including it in the key file with the action "invalid".
X
X
X     SSSSEEEEEEEE AAAALLLLSSSSOOOO
X          less(1)
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X     Page 3                                          (printed 9/14/89)
X
X
X
END_OF_FILE

mark@unix386.Convergent.COM (Mark Nudelman) (09/15/89)

#! /bin/sh
# This is a shell archive.
# Remove anything before this line, then unpack it
# by saving it into a file and typing "sh file".

echo shar: Extracting \"ch.c\"
sed "s/^X//" >'ch.c' <<'END_OF_FILE'
X/*
X * Low level character input from the input file.
X * We use these special purpose routines which optimize moving
X * both forward and backward from the current read pointer.
X */
X
X#include "less.h"
X
Xpublic int file = -1;		/* File descriptor of the input file */
X
X/*
X * Pool of buffers holding the most recently used blocks of the input file.
X */
X#define BUFSIZ	1024
Xstruct buf {
X	struct buf *next, *prev;
X	long block;
X	int datasize;
X	unsigned char data[BUFSIZ];
X};
Xpublic int nbufs;
X
X/*
X * The buffer pool is kept as a doubly-linked circular list,
X * in order from most- to least-recently used.
X * The circular list is anchored by buf_anchor.
X */
X#define	END_OF_CHAIN	((struct buf *)&buf_anchor)
X#define	buf_head	buf_anchor.next
X#define	buf_tail	buf_anchor.prev
X
Xstatic struct {
X	struct buf *next, *prev;
X} buf_anchor = { END_OF_CHAIN, END_OF_CHAIN };
X
Xextern int seven_bit;
Xextern int ispipe;
Xextern int autobuf;
Xextern int cbufs;
Xextern int sigs;
X#if LOGFILE
Xextern int logfile;
X#endif
X
Xstatic int ch_addbuf();
X
X/*
X * Current position in file, as seen by external users of ch functions.
X * Stored as a block number and an offset into the block.
X */
Xstatic long ch_block;
Xstatic int ch_offset;
X
X/*
X * Real current position in file, as manipulated by system calls (lseek).
X */
Xstatic POSITION ch_fpos;
X
X/* 
X * Length of file.
X */
Xstatic POSITION ch_fsize;
X
X
X/*
X * Get the character pointed to by the read pointer.
X * ch_get() is a macro which is more efficient to call
X * than fch_get (the function), in the usual case 
X * that the block desired is at the head of the chain.
X */
X#define	ch_get()   ((buf_head->block == ch_block && \
X		     ch_offset < buf_head->datasize) ? \
X			buf_head->data[ch_offset] : fch_get())
X	static int
Xfch_get()
X{
X	register struct buf *bp;
X	register int n;
X	register unsigned char *p;
X	POSITION pos;
X
X	/*
X	 * Look for a buffer holding the desired block.
X	 */
X	for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
X		if (bp->block == ch_block)
X		{
X			if (ch_offset >= bp->datasize)
X				/*
X				 * Need more data in this buffer.
X				 */
X				goto read_more;
X			goto found;
X		}
X	/*
X	 * Block is not in a buffer.  
X	 * Take the least recently used buffer 
X	 * and read the desired block into it.
X	 * If the LRU buffer has data in it, 
X	 * and autobuf is true, and input is a pipe, 
X	 * then try to allocate a new buffer first.
X	 */
X	if (autobuf && ispipe && buf_tail->block != (long)(-1))
X		if (ch_addbuf(1))
X			autobuf = 0;
X	bp = buf_tail;
X	bp->block = ch_block;
X	bp->datasize = 0;
X
X    read_more:
X	pos = (ch_block * BUFSIZ) + bp->datasize;
X	if (pos != ch_fpos)
X	{
X		/*
X		 * If input is a pipe, the data requested should 
X		 * be immediately after the last data read
X		 * (that is, there should be no need to seek).
X		 * If not, the data has been lost: just return "?".
X		 */
X		if (ispipe)
X			return ('?');
X		if (lseek(file, (offset_t)pos, 0) == BAD_LSEEK)
X		{
X 			error("seek error");
X 			quit();
X 		}
X 		ch_fpos = pos;
X 	}
X
X	/*
X	 * Read the block.
X	 * If we read less than a full block, we just return the
X	 * partial block and pick up the rest next time.
X	 */
X	n = iread(file, &bp->data[bp->datasize], BUFSIZ - bp->datasize);
X	if (n == READ_INTR)
X		return (EOI);
X	if (n < 0)
X	{
X		error("read error");
X		quit();
X	}
X	ch_fpos += n;
X
X#if LOGFILE
X	/*
X	 * If we have a log file, write the new data to it.
X	 */
X	if (logfile >= 0 && n > 0)
X		write(logfile, &bp->data[bp->datasize], n);
X#endif
X
X	bp->datasize += n;
X
X	/*
X	 * If we read to end of file, set an EOI marker in 
X	 * the buffered data itself.
X	 * Then ensure the data is "clean": there are no 
X	 * extra EOI chars in the data.
X	 */
X	if (n == 0)
X	{
X		ch_fsize = pos;
X		bp->data[bp->datasize++] = EOI;
X	}
X
X	p = &bp->data[bp->datasize];
X	while (--n >= 0)
X	{
X		p--;
X		if (seven_bit)
X			*p &= 0177;
X		if (*p == EOI)
X			*p = '@';
X	}
X
X    found:
X	if (buf_head != bp)
X	{
X		/*
X		 * Move the buffer to the head of the buffer chain.
X		 * This orders the buffer chain, most- to least-recently used.
X		 */
X		bp->next->prev = bp->prev;
X		bp->prev->next = bp->next;
X
X		bp->next = buf_head;
X		bp->prev = END_OF_CHAIN;
X		buf_head->prev = bp;
X		buf_head = bp;
X	}
X
X	if (ch_offset >= bp->datasize)
X		/*
X		 * After all that, we still don't have enough data.
X		 * Go back and try again.
X		 */
X		goto read_more;
X
X	return (bp->data[ch_offset]);
X}
X
X#if LOGFILE
X/*
X * Close the logfile.
X * If we haven't read all of standard input into it, do that now.
X */
X	public void
Xend_logfile()
X{
X	static int tried = 0;
X
X	if (logfile < 0)
X		return;
X	if (!tried && ch_fsize == NULL_POSITION)
X	{
X		tried = 1;
X		ierror("Finishing logfile");
X		while (ch_forw_get() != EOI)
X			if (sigs)
X				break;
X	}
X	close(logfile);
X	logfile = -1;
X}
X
X/*
X * Start a log file AFTER less has already been running.
X * Invoked from the - command; see toggle_option().
X * Write all the existing buffered data to the log file.
X */
X	public void
Xsync_logfile()
X{
X	register struct buf *bp;
X	register int n;
X	long block;
X	long last_block;
X
X	last_block = (ch_fpos + BUFSIZ - 1) / BUFSIZ;
X	for (block = 0;  block <= last_block;  block++)
X		for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
X			if (bp->block == block)
X			{
X				n = bp->datasize;
X				if (bp->data[n-1] == EOI)
X					n--;
X				write(logfile, bp->data, n);
X				break;
X			}
X}
X
X#endif
X
X/*
X * Determine if a specific block is currently in one of the buffers.
X */
X	static int
Xbuffered(block)
X	long block;
X{
X	register struct buf *bp;
X
X	for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
X		if (bp->block == block)
X			return (1);
X	return (0);
X}
X
X/*
X * Seek to a specified position in the file.
X * Return 0 if successful, non-zero if can't seek there.
X */
X	public int
Xch_seek(pos)
X	register POSITION pos;
X{
X	long new_block;
X
X	new_block = pos / BUFSIZ;
X	if (!ispipe || pos == ch_fpos || buffered(new_block))
X	{
X		/*
X		 * Set read pointer.
X		 */
X		ch_block = new_block;
X		ch_offset = pos % BUFSIZ;
X		return (0);
X	}
X	return (1);
X}
X
X/*
X * Seek to the end of the file.
X */
X	public int
Xch_end_seek()
X{
X	POSITION len;
X
X	len = ch_length();
X	if (len != NULL_POSITION)
X		return (ch_seek(len));
X
X	/*
X	 * Do it the slow way: read till end of data.
X	 */
X	while (ch_forw_get() != EOI)
X		if (sigs)
X			return (1);
X	return (0);
X}
X
X/*
X * Seek to the beginning of the file, or as close to it as we can get.
X * We may not be able to seek there if input is a pipe and the
X * beginning of the pipe is no longer buffered.
X */
X	public int
Xch_beg_seek()
X{
X	register struct buf *bp, *firstbp;
X
X	/*
X	 * Try a plain ch_seek first.
X	 */
X	if (ch_seek((POSITION)0) == 0)
X		return (0);
X
X	/*
X	 * Can't get to position 0.
X	 * Look thru the buffers for the one closest to position 0.
X	 */
X	firstbp = bp = buf_head;
X	if (bp == END_OF_CHAIN)
X		return (1);
X	while ((bp = bp->next) != END_OF_CHAIN)
X		if (bp->block < firstbp->block)
X			firstbp = bp;
X	ch_block = firstbp->block;
X	ch_offset = 0;
X	return (0);
X}
X
X/*
X * Return the length of the file, if known.
X */
X	public POSITION
Xch_length()
X{
X	return (ch_fsize);
X}
X
X/*
X * Return the current position in the file.
X */
X	public POSITION
Xch_tell()
X{
X	return (ch_block * BUFSIZ + ch_offset);
X}
X
X/*
X * Get the current char and post-increment the read pointer.
X */
X	public int
Xch_forw_get()
X{
X	register int c;
X
X	c = ch_get();
X	if (c != EOI && ++ch_offset >= BUFSIZ)
X	{
X		ch_offset = 0;
X		ch_block ++;
X	}
X	return (c);
X}
X
X/*
X * Pre-decrement the read pointer and get the new current char.
X */
X	public int
Xch_back_get()
X{
X	if (--ch_offset < 0)
X	{
X		if (ch_block <= 0 || (ispipe && !buffered(ch_block-1)))
X		{
X			ch_offset = 0;
X			return (EOI);
X		}
X		ch_offset = BUFSIZ - 1;
X		ch_block--;
X	}
X	return (ch_get());
X}
X
X/*
X * Allocate buffers.
X * Caller wants us to have a total of at least want_nbufs buffers.
X * keep==1 means keep the data in the current buffers;
X * otherwise discard the old data.
X */
X	public void
Xch_init(want_nbufs, keep)
X	int want_nbufs;
X	int keep;
X{
X	register struct buf *bp;
X	char message[80];
X
X	cbufs = nbufs;
X	if (nbufs < want_nbufs && ch_addbuf(want_nbufs - nbufs))
X	{
X		/*
X		 * Cannot allocate enough buffers.
X		 * If we don't have ANY, then quit.
X		 * Otherwise, just report the error and return.
X		 */
X		sprintf(message, "Cannot allocate %d buffers",
X			want_nbufs - nbufs);
X		error(message);
X		if (nbufs == 0)
X			quit();
X		return;
X	}
X
X	if (keep)
X		return;
X
X	/*
X	 * We don't want to keep the old data,
X	 * so initialize all the buffers now.
X	 */
X	for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
X		bp->block = (long)(-1);
X
X	/*
X	 * Figure out the size of the file, if we can.
X	 */
X	ch_fsize = (ispipe) ? NULL_POSITION : filesize(file);
X
X	/*
X	 * Seek to the beginning of the file.
X	 */
X	if (!ispipe)
X		(void) lseek(file, (offset_t)0, 0);
X	ch_fpos = (POSITION) 0;
X 
X	(void) ch_seek((POSITION)0);
X}
X
X/*
X * Allocate some new buffers.
X * The buffers are added to the tail of the buffer chain.
X */
X	static int
Xch_addbuf(nnew)
X	int nnew;
X{
X	register struct buf *bp;
X	register struct buf *newbufs;
X
X	/*
X	 * We don't have enough buffers.  
X	 * Allocate some new ones.
X	 */
X	newbufs = (struct buf *) calloc(nnew, sizeof(struct buf));
X	if (newbufs == NULL)
X		return (1);
X
X	/*
X	 * Initialize the new buffers and link them together.
X	 * Link them all onto the tail of the buffer list.
X	 */
X	nbufs += nnew;
X	cbufs = nbufs;
X	for (bp = &newbufs[0];  bp < &newbufs[nnew];  bp++)
X	{
X		bp->next = bp + 1;
X		bp->prev = bp - 1;
X		bp->block = (long)(-1);
X	}
X	newbufs[nnew-1].next = END_OF_CHAIN;
X	newbufs[0].prev = buf_tail;
X	buf_tail->next = &newbufs[0];
X	buf_tail = &newbufs[nnew-1];
X	return (0);
X}
END_OF_FILE
echo shar: Extracting \"command.c\"
sed "s/^X//" >'command.c' <<'END_OF_FILE'
X/*
X * User-level command processor.
X */
X
X#include "less.h"
X#include "position.h"
X#include "cmd.h"
X
X#define	NO_MCA		0
X#define	MCA_DONE	1
X#define	MCA_MORE	2
X
Xextern int erase_char, kill_char;
Xextern int ispipe;
Xextern int sigs;
Xextern int quit_at_eof;
Xextern int hit_eof;
Xextern int sc_width;
Xextern int sc_height;
Xextern int swindow;
Xextern int curr_ac;
Xextern int ac;
Xextern int quitting;
Xextern int scroll;
Xextern int nohelp;
Xextern char *first_cmd;
Xextern char *every_first_cmd;
Xextern char version[];
Xextern POSITION initial_pos;
X#if EDITOR
Xextern char *editor;
Xextern char *editproto;
X#endif
Xextern int screen_trashed;	/* The screen has been overwritten */
X
Xstatic char cmdbuf[120];	/* Buffer for holding a multi-char command */
X#if SHELL_ESCAPE
Xstatic char *shellcmd = NULL;	/* For holding last shell command for "!!" */
X#endif
Xstatic char *cp;		/* Pointer into cmdbuf */
Xstatic int cmd_col;		/* Current column of the multi-char command */
Xstatic int mca;			/* The multicharacter command (action) */
Xstatic int search_type;		/* The previous type of search */
Xstatic int number;		/* The number typed by the user */
Xstatic char optchar;
X
X/*
X * Reset command buffer (to empty).
X */
X	static void
Xcmd_reset()
X{
X	cp = cmdbuf;
X}
X
X/*
X * Backspace in command buffer.
X */
X	static int
Xcmd_erase()
X{
X	if (cp == cmdbuf)
X		/*
X		 * Backspace past beginning of the string:
X		 * this usually means abort the command.
X		 */
X		return (1);
X
X	if (control_char(*--cp))
X	{
X		/*
X		 * Erase an extra character, for the carat.
X		 */
X		backspace();
X		cmd_col--;
X	}
X	backspace();
X	cmd_col--;
X	return (0);
X}
X
X/*
X * Set up the display to start a new multi-character command.
X */
X	static void
Xstart_mca(action, prompt)
X	int action;
X	char *prompt;
X{
X	lower_left();
X	clear_eol();
X	putstr(prompt);
X	/*
X	 * {{ Assumes that the prompt contains only simple printable
X	 *    characters, so can use strlen() to get the column. }}
X	 */
X	cmd_col = strlen(prompt);
X	mca = action;
X}
X
X/*
X * Set up the display to start a new search command.
X */
X	static void
Xsearch_mca()
X{
X	switch (search_type)
X	{
X	case SRCH_FORW:
X		start_mca(A_F_SEARCH, "/");
X		break;
X	case SRCH_BACK:
X		start_mca(A_B_SEARCH, "?");
X		break;
X	case SRCH_FILE:
X		start_mca(A_T_SEARCH, "@/");
X		break;
X	case SRCH_FORW|SRCH_NOMATCH:
X		start_mca(A_F_SEARCH, "! /");
X		break;
X	case SRCH_BACK|SRCH_NOMATCH:
X		start_mca(A_B_SEARCH, "! ?");
X		break;
X	case SRCH_FILE|SRCH_NOMATCH:
X		start_mca(A_T_SEARCH, "! @/");
X		break;
X	}
X}
X
X/*
X * Process a single character of a multi-character command, such as
X * a number, or the pattern of a search command.
X */
X	static int
Xcmd_char(c)
X	int c;
X{
X	if (c == erase_char)
X	{
X		if (cmd_erase())
X			return (1);
X	} else if (c == kill_char)
X	{
X		/* {{ Could do this faster, but who cares? }} */
X		while (cmd_erase() == 0)
X			;
X	} else if (cp >= &cmdbuf[sizeof(cmdbuf)-1])
X	{
X		/*
X		 * No room in the command buffer.
X		 */
X		bell();
X	} else if (cmd_col >= sc_width-3)
X	{
X		/*
X		 * No room on the screen.
X		 * {{ Could get fancy here; maybe shift the displayed
X		 *    line and make room for more chars, like ksh. }}
X		 */
X		bell();
X	} else
X	{
X		/*
X		 * Append the character to the string.
X		 */
X		*cp++ = c;
X		if (control_char(c))
X		{
X			putchr('^');
X			cmd_col++;
X			c = carat_char(c);
X		}
X		putchr(c);
X		cmd_col++;
X	}
X	return (0);
X}
X
X/*
X * Return the number currently in the command buffer.
X */
X	static int
Xcmd_int()
X{
X	*cp = '\0';
X	cp = cmdbuf;
X	return (atoi(cmdbuf));
X}
X
X/*
X * Move the cursor to lower left before executing a command.
X * This looks nicer if the command takes a long time before
X * updating the screen.
X */
X	static void
Xcmd_exec()
X{
X	lower_left();
X	flush();
X}
X
X/*
X * Display the appropriate prompt.
X */
X	static void
Xprompt()
X{
X	register char *p;
X
X	if (first_cmd != NULL && *first_cmd != '\0')
X	{
X		/*
X		 * No prompt necessary if commands are from first_cmd
X		 * rather than from the user.
X		 */
X		return;
X	}
X
X	/*
X	 * If nothing is displayed yet, display starting from initial_pos.
X	 */
X	if (position(TOP) == NULL_POSITION)
X		jump_loc(initial_pos);
X	else if (screen_trashed)
X		repaint();
X
X	/*
X	 * If the -E flag is set and we've hit EOF on the last file, quit.
X	 */
X	if (quit_at_eof == 2 && hit_eof && curr_ac + 1 >= ac)
X		quit();
X
X	/*
X	 * Select the proper prompt and display it.
X	 */
X	lower_left();
X	clear_eol();
X	p = pr_string();
X	lower_left();
X	clear_eol();
X	if (p == NULL)
X		putchr(':');
X	else
X	{
X		so_enter();
X		putstr(p);
X		so_exit();
X	}
X}
X
X/*
X * Get command character.
X * The character normally comes from the keyboard,
X * but may come from the "first_cmd" string.
X */
X	static int
Xgetcc()
X{
X	if (first_cmd == NULL)
X		return (getchr());
X
X	if (*first_cmd != '\0')
X		return (*first_cmd++);
X	/*
X	 * (*first_cmd == '\0') means we reached end of first_cmd input.
X	 */
X	first_cmd = NULL;
X	if (cp == cmdbuf || position(TOP) != NULL_POSITION)
X		return (getchr());
X	/*
X	 * Command is incomplete, so try to complete it.
X	 * There are only two cases:
X	 * 1. We have "/string" but no newline.  Add the \n.
X	 * 2. We have a number but no command.  Treat as #g.
X	 * (This is all pretty hokey.)
X	 */
X	if (mca != A_DIGIT)
X		/* Not a number; must be search string */
X		return ('\n'); 
X	else
X		/* A number; append a 'g' */
X		return ('g');
X}
X
X/*
X * Execute a multicharacter command.
X */
X	static void
Xexec_mca()
X{
X	register char *p;
X
X	*cp = '\0';
X	cmd_exec();
X	switch (mca)
X	{
X	case A_F_SEARCH:
X	case A_B_SEARCH:
X	case A_T_SEARCH:
X		search(search_type, cmdbuf, number);
X		/*
X		 * If this was a search from beginning of file, change
X		 * to a forward search, so that A_AGAIN_SEARCH will
X		 * search from where we are, not from beginning of file.
X		 */
X		if (SRCH_TYPE(search_type) == SRCH_FILE)
X			search_type = SRCH_FORW | SRCH_FLAG(search_type);
X		break;
X	case A_FIRSTCMD:
X		/*
X		 * Skip leading spaces or + signs in the string.
X		 */
X		for (p = cmdbuf;  *p == '+' || *p == ' ';  p++)
X			;
X		if (every_first_cmd != NULL)
X			free(every_first_cmd);
X		if (*p == '\0')
X			every_first_cmd = NULL;
X		else
X			every_first_cmd = save(p);
X		break;
X	case A_TOGGLE_OPTION:
X		toggle_option(optchar, cmdbuf, 1);
X		optchar = '\0';
X		break;
X	case A_EXAMINE:
X		/*
X		 * Ignore leading spaces and glob the filename.
X		 */
X		p = glob(skipsp(cmdbuf));
X		if (p == NULL)
X			break;
X		edit(p);
X		free(p);
X		break;
X#if SHELL_ESCAPE
X	case A_SHELL:
X		/*
X		 * !! just uses whatever is in shellcmd.
X		 * Otherwise, copy cmdbuf to shellcmd,
X		 * expanding any special characters ("%" or "#").
X		 */
X		if (*cmdbuf != '!')
X		{
X			if (shellcmd != NULL)
X				free(shellcmd);
X			shellcmd = fexpand(cmdbuf);
X			if (shellcmd == NULL)
X				break;
X		}
X
X		if (shellcmd == NULL)
X			lsystem("");
X		else
X			lsystem(shellcmd);
X		error("!done");
X		break;
X#endif
X	}
X}
X
X/*
X * Add a character to a multi-character command.
X */
X	static int
Xmca_char(c)
X	int c;
X{
X	char *p;
X	char buf[3];
X
X	switch (mca)
X	{
X	case 0:
X		/*
X		 * Not in a multicharacter command.
X		 */
X		return (NO_MCA);
X
X	case A_PREFIX:
X		/*
X		 * In the prefix of a command.
X		 */
X		return (NO_MCA);
X
X	case A_DIGIT:
X		/*
X		 * Entering digits of a number.
X		 * Terminated by a non-digit.
X		 */
X		if ((c < '0' || c > '9') &&
X			c != erase_char && c != kill_char)
X		{
X			/*
X			 * Not part of the number.
X			 * Treat as a normal command character.
X			 */
X			number = cmd_int();
X			mca = 0;
X			return (NO_MCA);
X		}
X		break;
X
X	case A_TOGGLE_OPTION:
X		/*
X		 * Special case for the TOGGLE_OPTION command.
X		 * If the option letter which was entered is a
X		 * single-char option, execute the command immediately,
X		 * so user doesn't have to hit RETURN.
X		 */
X		if (optchar == '\0' && c != erase_char && c != kill_char)
X		{
X			optchar = c;
X			if (single_char_option(c))
X			{
X				toggle_option(c, "", 1);
X				return (MCA_DONE);
X			}
X			/*
X			 * Display an option-specific prompt.
X			 */
X			if ((p = opt_prompt(c)) == NULL)
X			{
X				buf[0] = '-';
X				buf[1] = c;
X				buf[2] = '\0';
X				p = buf;
X			}
X			start_mca(A_TOGGLE_OPTION, p);
X			return (MCA_MORE);
X		}
X		break;
X
X	case A_F_SEARCH:
X	case A_B_SEARCH:
X	case A_T_SEARCH:
X		/*
X		 * Special case for search commands.
X		 * A ! as the first character of the pattern
X		 * means invert the sense of the search.
X		 * Toggle the NOMATCH flag and get a new
X		 * character for the start of the pattern.
X		 */
X		if (cp == cmdbuf && c == '!')
X		{
X			search_type ^= SRCH_NOMATCH;
X			search_mca();
X			return (MCA_MORE);
X		}
X		break;
X	}
X
X	/*
X	 * Any other multicharacter command
X	 * is terminated by a newline.
X	 */
X	if (c == '\n' || c == '\r')
X	{
X		/*
X		 * Execute the command.
X		 */
X		exec_mca();
X		return (MCA_DONE);
X	}
X	/*
X	 * Append the char to the command buffer.
X	 */
X	if (cmd_char(c))
X		/*
X		 * Abort the multi-char command.
X		 */
X		return (MCA_DONE);
X	/*
X	 * Need another character.
X	 */
X	return (MCA_MORE);
X}
X
X/*
X * Main command processor.
X * Accept and execute commands until a quit command, then return.
X */
X	public void
Xcommands()
X{
X	register int c;
X	register int action;
X
X	search_type = SRCH_FORW;
X	scroll = (sc_height + 1) / 2;
X
X	for (;;)
X	{
X		mca = 0;
X		number = 0;
X		optchar = '\0';
X
X		/*
X		 * See if any signals need processing.
X		 */
X		if (sigs)
X		{
X			psignals();
X			if (quitting)
X				quit();
X		}
X			
X		/*
X		 * Display prompt and accept a character.
X		 */
X		cmd_reset();
X		prompt();
X		noprefix();
X		if (sigs)
X			continue;
X		c = getcc();
X
X	again:
X		if (sigs)
X			continue;
X
X		/*
X		 * If we are in a multicharacter command, call mca_char.
X		 * Otherwise we call cmd_decode to determine the
X		 * action to be performed.
X		 */
X		if (mca)
X			switch (mca_char(c))
X			{
X			case MCA_MORE:
X				/*
X				 * Need another character.
X				 */
X				c = getcc();
X				goto again;
X			case MCA_DONE:
X				/*
X				 * Command has been handled by mca_char.
X				 * Start clean with a prompt.
X				 */
X				continue;
X			case NO_MCA:
X				/*
X				 * Not a multi-char command
X				 * (at least, not anymore).
X				 */
X				break;
X			}
X
X		/*
X		 * Decode the command character and decide what to do.
X		 */
X		switch (action = cmd_decode(c))
X		{
X		case A_DIGIT:
X			/*
X			 * First digit of a number.
X			 */
X			start_mca(A_DIGIT, ":");
X			goto again;
X
X		case A_F_WINDOW:
X			/*
X			 * Forward one window (and set the window size).
X			 */
X			if (number > 0)
X				swindow = number;
X			/* FALLTHRU */
X		case A_F_SCREEN:
X			/*
X			 * Forward one screen.
X			 */
X			if (number <= 0)
X				number = swindow;
X			cmd_exec();
X			forward(number, 1);
X			break;
X
X		case A_B_WINDOW:
X			/*
X			 * Backward one window (and set the window size).
X			 */
X			if (number > 0)
X				swindow = number;
X			/* FALLTHRU */
X		case A_B_SCREEN:
X			/*
X			 * Backward one screen.
X			 */
X			if (number <= 0)
X				number = swindow;
X			cmd_exec();
X			backward(number, 1);
X			break;
X
X		case A_F_LINE:
X			/*
X			 * Forward N (default 1) line.
X			 */
X			if (number <= 0)
X				number = 1;
X			cmd_exec();
X			forward(number, 0);
X			break;
X
X		case A_B_LINE:
X			/*
X			 * Backward N (default 1) line.
X			 */
X			if (number <= 0)
X				number = 1;
X			cmd_exec();
X			backward(number, 0);
X			break;
X
X		case A_F_SCROLL:
X			/*
X			 * Forward N lines 
X			 * (default same as last 'd' or 'u' command).
X			 */
X			if (number > 0)
X				scroll = number;
X			cmd_exec();
X			forward(scroll, 0);
X			break;
X
X		case A_B_SCROLL:
X			/*
X			 * Forward N lines 
X			 * (default same as last 'd' or 'u' command).
X			 */
X			if (number > 0)
X				scroll = number;
X			cmd_exec();
X			backward(scroll, 0);
X			break;
X
X		case A_FREPAINT:
X			/*
X			 * Flush buffers, then repaint screen.
X			 * Don't flush the buffers on a pipe!
X			 */
X			if (!ispipe)
X			{
X				ch_init(0, 0);
X				clr_linenum();
X			}
X			/* FALLTHRU */
X		case A_REPAINT:
X			/*
X			 * Repaint screen.
X			 */
X			cmd_exec();
X			repaint();
X			break;
X
X		case A_GOLINE:
X			/*
X			 * Go to line N, default beginning of file.
X			 */
X			if (number <= 0)
X				number = 1;
X			cmd_exec();
X			jump_back(number);
X			break;
X
X		case A_PERCENT:
X			/*
X			 * Go to a specified percentage into the file.
X			 */
X			if (number < 0)
X				number = 0;
X			if (number > 100)
X				number = 100;
X			cmd_exec();
X			jump_percent(number);
X			break;
X
X		case A_GOEND:
X			/*
X			 * Go to line N, default end of file.
X			 */
X			cmd_exec();
X			if (number <= 0)
X				jump_forw();
X			else
X				jump_back(number);
X			break;
X
X		case A_STAT:
X			/*
X			 * Print file name, etc.
X			 */
X			cmd_exec();
X			error(eq_message());
X			break;
X			
X		case A_VERSION:
X			/*
X			 * Print version number, without the "@(#)".
X			 */
X			cmd_exec();
X			error(version+4);
X			break;
X
X		case A_QUIT:
X			/*
X			 * Exit.
X			 */
X			quit();
X
X		case A_T_SEARCH:
X			search_type = SRCH_FILE;
X			goto do_search;
X		case A_B_SEARCH:
X			search_type = SRCH_BACK;
X			goto do_search;
X		case A_F_SEARCH:
X			search_type = SRCH_FORW;
X		do_search:
X			/*
X			 * Search for a pattern.
X			 * Get the first char of the pattern.
X			 */
X			if (number <= 0)
X				number = 1;
X			search_mca();
X			c = getcc();
X			goto again;
X
X		case A_REVERSE_SEARCH:
X			/*
X			 * Repeat previous search, in reverse direction.
X			 */
X			c = SRCH_FLAG(search_type);
X			if (SRCH_TYPE(search_type) == SRCH_BACK)
X				search_type = SRCH_FORW;
X			else
X				search_type = SRCH_BACK;
X			search_type |= c;
X			/*FALLTHRU*/
X		case A_AGAIN_SEARCH:
X			/*
X			 * Repeat previous search.
X			 */
X			if (number <= 0)
X				number = 1;
X			search_mca();
X			cmd_exec();
X			search(search_type, (char *)NULL, number);
X			break;
X		
X		case A_HELP:
X			/*
X			 * Help.
X			 */
X			if (nohelp)
X			{
X				bell();
X				break;
X			}
X			lower_left();
X			clear_eol();
X			putstr("help");
X			cmd_exec();
X			help();
X			break;
X
X		case A_EXAMINE:
X			/*
X			 * Edit a new file.  Get the filename.
X			 */
X			cmd_reset();
X			start_mca(A_EXAMINE, "Examine: ");
X			c = getcc();
X			goto again;
X			
X		case A_VISUAL:
X			/*
X			 * Invoke an editor on the input file.
X			 */
X#if EDITOR
X		{
X			if (ispipe)
X			{
X				error("Cannot edit standard input");
X				break;
X			}
X			/*
X			 * Expand the editor prototype string
X			 * and pass it to the system to execute.
X			 */
X			cmd_exec();
X			lsystem(pr_expand(editproto, 0));
X			/*
X			 * Throw away any buffered data, 
X			 * since the file was edited.
X			 */
X			ch_init(0, 0);
X			clr_linenum();
X			break;
X		}
X#else
X			error("Command not available");
X			break;
X#endif
X
X		case A_NEXT_FILE:
X			/*
X			 * Examine next file.
X			 */
X			if (number <= 0)
X				number = 1;
X			next_file(number);
X			break;
X
X		case A_PREV_FILE:
X			/*
X			 * Examine previous file.
X			 */
X			if (number <= 0)
X				number = 1;
X			prev_file(number);
X			break;
X
X		case A_TOGGLE_OPTION:
X			/*
X			 * Toggle a flag setting.
X			 */
X			cmd_reset();
X			start_mca(A_TOGGLE_OPTION, "-");
X			c = getcc();
X			goto again;
X
X		case A_DISP_OPTION:
X			/*
X			 * Report a flag setting.
X			 */
X			cmd_reset();
X			start_mca(A_DISP_OPTION, "_");
X			c = getcc();
X			if (c == erase_char || c == kill_char)
X				break;
X			toggle_option(c, "", 0);
X			break;
X
X		case A_FIRSTCMD:
X			/*
X			 * Set an initial command for new files.
X			 */
X			cmd_reset();
X			start_mca(A_FIRSTCMD, "+");
X			c = getcc();
X			goto again;
X
X		case A_SHELL:
X			/*
X			 * Shell escape.
X			 */
X#if SHELL_ESCAPE
X			cmd_reset();
X			start_mca(A_SHELL, "!");
X			c = getcc();
X			goto again;
X#else
X			error("Command not available");
X			break;
X#endif
X
X		case A_SETMARK:
X			/*
X			 * Set a mark.
X			 */
X			start_mca(A_SETMARK, "mark: ");
X			c = getcc();
X			if (c == erase_char || c == kill_char)
X				break;
X			setmark(c);
X			break;
X
X		case A_GOMARK:
X			/*
X			 * Go to a mark.
X			 */
X			start_mca(A_GOMARK, "goto mark: ");
X			c = getcc();
X			if (c == erase_char || c == kill_char)
X				break;
X			gomark(c);
X			break;
X
X		case A_ANYBRAC:
X			/*
X			 * Find matching bracket.
X			 */
X	/* TEMP */
X	if (number > 0) 
X	{
X		error("% is now used for bracket matching; use p for percent");
X		break;
X	}
X	/* end TEMP */
X			cmd_exec();
X			match_brac('\0', 1);
X			break;
X
X		case A_BRACLP:
X			/*
X			 * Go to matching left parenthesis.
X			 */
X			cmd_exec();
X			match_brac('(', number);
X			break;
X
X		case A_BRACRP:
X			/*
X			 * Go to matching right parenthesis.
X			 */
X			cmd_exec();
X			match_brac(')', number);
X			break;
X
X		case A_BRACLS:
X			/*
X			 * Go to matching left square bracket.
X			 */
X			cmd_exec();
X			match_brac('[', number);
X			break;
X
X		case A_BRACRS:
X			/*
X			 * Go to matching right square bracket.
X			 */
X			cmd_exec();
X			match_brac(']', number);
X			break;
X
X		case A_BRACLC:
X			/*
X			 * Go to matching left curly bracket.
X			 */
X			cmd_exec();
X			match_brac('{', number);
X			break;
X
X		case A_BRACRC:
X			/*
X			 * Go to matching right curly bracket.
X			 */
X			cmd_exec();
X			match_brac('}', number);
X			break;
X
X		case A_PREFIX:
X			/*
X			 * The command is incomplete (more chars are needed).
X			 * Display the current char, so the user knows
X			 * what's going on, and get another character.
X			 */
X			if (mca != A_PREFIX)
X				start_mca(A_PREFIX, "* ");
X			if (c == '\33')
X			{
X				/*
X				 * ESCAPE is special: we print "ESC"
X				 * rather than "^[".  We don't have
X				 * to worry about backspacing over it,
X				 * because prefix chars are never erased.
X				 */
X				putstr("ESC");
X			} else if (control_char(c))
X			{
X				putchr('^');
X				putchr(carat_char(c));
X			} else
X				putchr(c);
X			c = getcc();
X			goto again;
X
X		default:
X			if (action & A_1_TOGGLE_OPTION)
X			{
X				cmd_reset();
X				start_mca(A_TOGGLE_OPTION, "-");
X				c = action & ~A_1_TOGGLE_OPTION;
X				goto again;
X			}
X			bell();
X			break;
X		}
X	}
X}
END_OF_FILE
echo shar: Extracting \"decode.c\"
sed "s/^X//" >'decode.c' <<'END_OF_FILE'
X/*
X * Routines to decode user commands.
X *
X * This is all table driven.
X * A command table is a sequence of command descriptors.
X * Each command descriptor is a sequence of bytes with the following format:
X *	<c1><c2>...<cN><0><action>
X * The characters c1,c2,...,cN are the command string; that is,
X * the characters which the user must type.
X * It is terminated by a null <0> byte.
X * The byte after the null byte is the action code associated
X * with the command string.
X *
X * There may be many command tables.
X * The first (default) table is built-in.
X * Other tables are read in from "lesskey" files.
X * All the tables are linked together and are searched in order.
X */
X
X#include "less.h"
X#include "cmd.h"
X
X#define	ESC	CONTROL('[')
X
X/*
X * Command table is ordered roughly according to expected
X * frequency of use, so the common commands are near the beginning.
X */
Xstatic char cmdtable[] =
X{
X	'\r',0,				A_F_LINE,
X	'\n',0,				A_F_LINE,
X	'e',0,				A_F_LINE,
X	'j',0,				A_F_LINE,
X	CONTROL('E'),0,			A_F_LINE,
X	CONTROL('N'),0,			A_F_LINE,
X	'k',0,				A_B_LINE,
X	'y',0,				A_B_LINE,
X	CONTROL('Y'),0,			A_B_LINE,
X	CONTROL('K'),0,			A_B_LINE,
X	CONTROL('P'),0,			A_B_LINE,
X	'd',0,				A_F_SCROLL,
X	CONTROL('D'),0,			A_F_SCROLL,
X	'u',0,				A_B_SCROLL,
X	CONTROL('U'),0,			A_B_SCROLL,
X	' ',0,				A_F_SCREEN,
X	'f',0,				A_F_SCREEN,
X	CONTROL('F'),0,			A_F_SCREEN,
X	CONTROL('V'),0,			A_F_SCREEN,
X	'b',0,				A_B_SCREEN,
X	CONTROL('B'),0,			A_B_SCREEN,
X	ESC,'v',0,			A_B_SCREEN,
X	'z',0,				A_F_WINDOW,
X	'w',0,				A_B_WINDOW,
X	'R',0,				A_FREPAINT,
X	'r',0,				A_REPAINT,
X	CONTROL('R'),0,			A_REPAINT,
X	CONTROL('L'),0,			A_REPAINT,
X	'g',0,				A_GOLINE,
X	'<',0,				A_GOLINE,
X	ESC,'<',0,			A_GOLINE,
X	'p',0,				A_PERCENT,
X	'%',0,				A_ANYBRAC,
X	'{',0,				A_BRACLC,
X	'}',0,				A_BRACRC,
X	'(',0,				A_BRACLP,
X	')',0,				A_BRACRP,
X	'[',0,				A_BRACLS,
X	']',0,				A_BRACRS,
X	'G',0,				A_GOEND,
X	ESC,'>',0,			A_GOEND,
X	'>',0,				A_GOEND,
X
X	'0',0,				A_DIGIT,
X	'1',0,				A_DIGIT,
X	'2',0,				A_DIGIT,
X	'3',0,				A_DIGIT,
X	'4',0,				A_DIGIT,
X	'5',0,				A_DIGIT,
X	'6',0,				A_DIGIT,
X	'7',0,				A_DIGIT,
X	'8',0,				A_DIGIT,
X	'9',0,				A_DIGIT,
X
X	'=',0,				A_STAT,
X	CONTROL('G'),0,			A_STAT,
X	':','f',0,			A_STAT,
X	'/',0,				A_F_SEARCH,
X	'?',0,				A_B_SEARCH,
X	ESC,'/',0,			A_T_SEARCH,
X	'n',0,				A_AGAIN_SEARCH,
X	ESC,'n',0,			A_REVERSE_SEARCH,
X	'm',0,				A_SETMARK,
X	'\'',0,				A_GOMARK,
X	CONTROL('X'),CONTROL('X'),0,	A_GOMARK,
X	'E',0,				A_EXAMINE,
X	':','e',0,			A_EXAMINE,
X	CONTROL('X'),CONTROL('V'),0,	A_EXAMINE,
X	'N',0,				A_NEXT_FILE,
X	'P',0,				A_PREV_FILE,
X	':','n',0,			A_NEXT_FILE,
X	':','p',0,			A_PREV_FILE,
X	'-',0,				A_TOGGLE_OPTION,
X	':','t','a',0,			A_1_TOGGLE_OPTION|('t'),
X	's',0,				A_1_TOGGLE_OPTION|('l'),
X	'_',0,				A_DISP_OPTION,
X	'v',0,				A_VISUAL,
X	'!',0,				A_SHELL,
X	'+',0,				A_FIRSTCMD,
X
X	'H',0,				A_HELP,
X	'h',0,				A_HELP,
X	'V',0,				A_VERSION,
X	'q',0,				A_QUIT,
X	':','q',0,			A_QUIT,
X	':','Q',0,			A_QUIT,
X	'Z','Z',0,			A_QUIT
X};
X
X/*
X * Structure to support a list of command tables.
X */
Xstruct tablelist
X{
X	struct tablelist *t_next;
X	char *t_start;
X	char *t_end;
X};
X
X/*
X * Structure for the default command table.
X */
Xstatic struct tablelist deftable = 
X	{ NULL, cmdtable, cmdtable+sizeof(cmdtable) };
X
X/*
X * List of tables; initially contains only the default table.
X */
Xstatic struct tablelist *tables = &deftable;
X
X/*
X * Buffer to hold current command string.
X */
Xstatic char kbuf[MAX_CMDLEN+1];
Xstatic char *kp = kbuf;
X
Xstatic int cmd_search();
X
X/*
X * Decode a command character and return the associated action.
X */
X	public int
Xcmd_decode(c)
X	int c;
X{
X	register struct tablelist *t;
X	register int action;
X
X	/*
X	 * Append the new command character to the command string in kbuf.
X	 */
X	*kp++ = c;
X	*kp = '\0';
X
X	/*
X	 * Search thru all the command tables.
X	 * Stop when we find an action which is not A_INVALID.
X	 */
X	for (t = tables;  t != NULL;  t = t->t_next)
X	{
X		action = cmd_search(t->t_start, t->t_end);
X		if (action != A_INVALID)
X			break;
X	}
X
X	if (action != A_PREFIX)
X		/*
X		 * This is not a prefix character.
X		 */
X		noprefix();
X
X	return (action);
X}
X
X/*
X * Indicate that we're not in a prefix command
X * by resetting the command buffer pointer.
X */
X	public void
Xnoprefix()
X{
X	kp = kbuf;
X}
X
X/*
X * Search a command table for the current command string (in kbuf).
X */
X	static int
Xcmd_search(table, endtable)
X	char *table;
X	char *endtable;
X{
X	register char *p;
X	register char *q;
X
X	for (p = table, q = kbuf;  p < endtable;  p++, q++)
X	{
X		if (*p == *q)
X		{
X			/*
X			 * Current characters match.
X			 * If we're at the end of the string, we've found it.
X			 * Return the action code, which is the character
X			 * after the null at the end of the string
X			 * in the command table.
X			 */
X			if (*p == '\0')
X				return (p[1] & 0377);
X		} else if (*q == '\0')
X		{
X			/*
X			 * Hit the end of the user's command,
X			 * but not the end of the string in the command table.
X			 * The user's command is incomplete.
X			 */
X			return (A_PREFIX);
X		} else
X		{
X			/*
X			 * Not a match.
X			 * Skip ahead to the next command in the
X			 * command table, and reset the pointer
X			 * to the user's command.
X			 */
X			while (*p++ != '\0') ;
X			q = kbuf-1;
X		}
X	}
X	/*
X	 * No match found in the entire command table.
X	 */
X	return (A_INVALID);
X}
X
X#if USERFILE
X/*
X * Set up a user command table, based on a "lesskey" file.
X */
X	public int
Xadd_cmdtable(filename)
X	char *filename;
X{
X	register struct tablelist *t;
X	register POSITION len;
X	register long n;
X	register int f;
X
X	/*
X	 * Try to open the lesskey file.
X	 * If we can't, return an error.
X	 */
X	f = open(filename, 0);
X	if (f < 0)
X		return (-1);
X
X	/*
X	 * Read the file into the user table.
X	 * We first figure out the size of the file and allocate space for it.
X	 * {{ Minimal error checking is done here.
X	 *    A garbage .less file will produce strange results.
X	 *    To avoid a large amount of error checking code here, we
X	 *    rely on the lesskey program to generate a good .less file. }}
X	 */
X	len = filesize(f);
X	if (len == NULL_POSITION || len < 3)
X	{
X		/*
X		 * Bad file (valid file must have at least 3 chars).
X		 */
X		close(f);
X		return (-1);
X	}
X	if ((t = (struct tablelist *) 
X			calloc(1, sizeof(struct tablelist))) == NULL)
X	{
X		close(f);
X		return (-1);
X	}
X	if ((t->t_start = (char *) calloc(len, sizeof(char))) == NULL)
X	{
X		free((char *)t);
X		close(f);
X		return (-1);
X	}
X	if (lseek(f, (offset_t)0, 0) == BAD_LSEEK)
X	{
X		free(t->t_start);
X		free((char *)t);
X		close(f);
X		return (-1);
X	}
X	n = read(f, t->t_start, len);
X	close(f);
X
X	if (n != len || t->t_start[n-2] != '\0')
X	{
X		/*
X		 * Several error cases are lumped together here:
X		 * - Cannot read user file (n < 0).
X		 * - User file is too short (a valid file must
X		 *   have at least 3 chars: one char command string,
X		 *   the terminating null byte, and the action byte).
X		 * - The final entry in the user file is bad (it
X		 *   doesn't have a null byte in the proper place).
X		 * Many other error cases are not caught, such as
X		 * invalid format in any except the last entry,
X		 * invalid action codes, command strings too long, etc.
X		 */
X		free(t->t_start);
X		free((char *)t);
X		return (-1);
X	}
X	t->t_end = t->t_start + n;
X	t->t_next = tables;
X	tables = t;
X	return (0);
X}
X
X/*
X * Try to add the lesskey file "$HOME/.less"
X */
X	public void
Xadd_hometable()
X{
X	char *homedir;
X	char *filename;
X	extern char *getenv();
X
X	homedir = getenv("HOME");
X	if (homedir == NULL)
X		return;
X	filename = (char *) calloc(strlen(homedir)+7, sizeof(char));
X	if (filename == NULL)
X		return;
X	sprintf(filename, "%s/.less", homedir);
X
X	/*
X	 * Ignore errors.
X	 */
X	(void) add_cmdtable(filename);
X
X	free(filename);
X}
X#endif
END_OF_FILE
echo shar: Extracting \"help.c\"
sed "s/^X//" >'help.c' <<'END_OF_FILE'
X#include  "less.h"
X
X/*
X * Display some help.
X * Just invoke another "less" to display the help file.
X *
X * {{ This makes this function very simple, and makes changing the
X *    help file very easy, but it may present difficulties on
X *    (non-Unix) systems which do not supply the "system()" function. }}
X */
X
X	public void
Xhelp()
X{
X	char cmd[sizeof(HELPFILE)+150];
X
X	sprintf(cmd, 
X	 "-less -m -H -+E -+s '-PmHELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done ' %s",
X	 HELPFILE);
X	lsystem(cmd);
X	error("End of help");
X}
END_OF_FILE
echo shar: Extracting \"input.c\"
sed "s/^X//" >'input.c' <<'END_OF_FILE'
X/*
X * High level routines dealing with getting lines of input 
X * from the file being viewed.
X *
X * When we speak of "lines" here, we mean PRINTABLE lines;
X * lines processed with respect to the screen width.
X * We use the term "raw line" to refer to lines simply
X * delimited by newlines; not processed with respect to screen width.
X */
X
X#include "less.h"
X
Xextern int squeeze;
Xextern int sigs;
X
X/*
X * Get the next line.
X * A "current" position is passed and a "new" position is returned.
X * The current position is the position of the first character of
X * a line.  The new position is the position of the first character
X * of the NEXT line.  The line obtained is the line starting at curr_pos.
X */
X	public POSITION
Xforw_line(curr_pos)
X	POSITION curr_pos;
X{
X	POSITION new_pos;
X	register int c;
X	int blankline;
X
X	if (curr_pos == NULL_POSITION || ch_seek(curr_pos))
X	{
X		null_line();
X		return (NULL_POSITION);
X	}
X
X	prewind();
X	plinenum(curr_pos);
X	(void) ch_seek(curr_pos);
X
X	c = ch_forw_get();
X	if (c == EOI)
X	{
X		null_line();
X		return (NULL_POSITION);
X	}
X	blankline = (c == '\n');
X
X	for (;;)
X	{
X		if (sigs)
X		{
X			null_line();
X			return (NULL_POSITION);
X		}
X		if (c == '\n' || c == EOI)
X		{
X			/*
X			 * End of the line.
X			 */
X			new_pos = ch_tell();
X			break;
X		}
X
X		/*
X		 * Append the char to the line and get the next char.
X		 */
X		if (pappend(c))
X		{
X			/*
X			 * The char won't fit in the line; the line
X			 * is too long to print in the screen width.
X			 * End the line here.
X			 */
X			new_pos = ch_tell() - 1;
X			break;
X		}
X		c = ch_forw_get();
X	}
X	(void) pappend('\0');
X
X	if (squeeze && blankline)
X	{
X		/*
X		 * This line is blank.
X		 * Skip down to the last contiguous blank line
X		 * and pretend it is the one which we are returning.
X		 */
X		while ((c = ch_forw_get()) == '\n')
X			if (sigs)
X			{
X				null_line();
X				return (NULL_POSITION);
X			}
X		if (c != EOI)
X			(void) ch_back_get();
X		new_pos = ch_tell();
X	}
X
X	return (new_pos);
X}
X
X/*
X * Get the previous line.
X * A "current" position is passed and a "new" position is returned.
X * The current position is the position of the first character of
X * a line.  The new position is the position of the first character
X * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
X */
X	public POSITION
Xback_line(curr_pos)
X	POSITION curr_pos;
X{
X	POSITION new_pos, begin_new_pos;
X	int c;
X
X	if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
X		ch_seek(curr_pos-1))
X	{
X		null_line();
X		return (NULL_POSITION);
X	}
X
X	if (squeeze)
X	{
X		/*
X		 * Find out if the "current" line was blank.
X		 */
X		(void) ch_forw_get();	/* Skip the newline */
X		c = ch_forw_get();	/* First char of "current" line */
X		(void) ch_back_get();	/* Restore our position */
X		(void) ch_back_get();
X
X		if (c == '\n')
X		{
X			/*
X			 * The "current" line was blank.
X			 * Skip over any preceding blank lines,
X			 * since we skipped them in forw_line().
X			 */
X			while ((c = ch_back_get()) == '\n')
X				if (sigs)
X				{
X					null_line();
X					return (NULL_POSITION);
X				}
X			if (c == EOI)
X			{
X				null_line();
X				return (NULL_POSITION);
X			}
X			(void) ch_forw_get();
X		}
X	}
X
X	/*
X	 * Scan backwards until we hit the beginning of the line.
X	 */
X	for (;;)
X	{
X		if (sigs)
X		{
X			null_line();
X			return (NULL_POSITION);
X		}
X		c = ch_back_get();
X		if (c == '\n')
X		{
X			/*
X			 * This is the newline ending the previous line.
X			 * We have hit the beginning of the line.
X			 */
X			new_pos = ch_tell() + 1;
X			break;
X		}
X		if (c == EOI)
X		{
X			/*
X			 * We have hit the beginning of the file.
X			 * This must be the first line in the file.
X			 * This must, of course, be the beginning of the line.
X			 */
X			new_pos = ch_tell();
X			break;
X		}
X	}
X
X	/*
X	 * Now scan forwards from the beginning of this line.
X	 * We keep discarding "printable lines" (based on screen width)
X	 * until we reach the curr_pos.
X	 *
X	 * {{ This algorithm is pretty inefficient if the lines
X	 *    are much longer than the screen width, 
X	 *    but I don't know of any better way. }}
X	 */
X	if (ch_seek(new_pos))
X	{
X		null_line();
X		return (NULL_POSITION);
X	}
X    loop:
X	begin_new_pos = new_pos;
X	prewind();
X	plinenum(new_pos);
X	(void) ch_seek(new_pos);
X
X	do
X	{
X		c = ch_forw_get();
X		if (c == EOI || sigs)
X		{
X			null_line();
X			return (NULL_POSITION);
X		}
X		new_pos++;
X		if (c == '\n')
X			break;
X		if (pappend(c))
X		{
X			/*
X			 * Got a full printable line, but we haven't
X			 * reached our curr_pos yet.  Discard the line
X			 * and start a new one.
X			 */
X			(void) pappend('\0');
X			(void) ch_back_get();
X			new_pos--;
X			goto loop;
X		}
X	} while (new_pos < curr_pos);
X
X	(void) pappend('\0');
X
X	return (begin_new_pos);
X}
END_OF_FILE
echo shar: Extracting \"line.c\"
sed "s/^X//" >'line.c' <<'END_OF_FILE'
X/*
X * Routines to manipulate the "line buffer".
X * The line buffer holds a line of output as it is being built
X * in preparation for output to the screen.
X * We keep track of the PRINTABLE length of the line as it is being built.
X */
X
X#include "less.h"
X
Xstatic char linebuf[1024];	/* Buffer which holds the current output line */
Xstatic char xlinebuf[1024];	/* Extension of linebuf to hold high bits */
Xstatic int curr;		/* Index into linebuf */
Xstatic int column;		/* Printable length, accounting for
X				   backspaces, etc. */
Xstatic int is_null_line;	/* */
X
X/*
X * A ridiculously complex state machine takes care of backspaces 
X * when in BS_SPECIAL mode.  The complexity arises from the attempt
X * to deal with all cases, especially involving long lines with underlining,
X * boldfacing or whatever.  There are still some cases which will break it.
X *
X * There are four states:
X *	LN_NORMAL is the normal state (not in underline mode).
X *	LN_UNDERLINE means we are in underline mode.  We expect to get
X *		either a sequence like "_\bX" or "X\b_" to continue
X *		underline mode, or anything else to end underline mode.
X *	LN_BOLDFACE means we are in boldface mode.  We expect to get sequences
X *		like "X\bX\b...X\bX" to continue boldface mode, or anything
X *		else to end boldface mode.
X *	LN_UL_X means we are one character after LN_UNDERLINE
X *		(we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
X *	LN_UL_XB means we are one character after LN_UL_X 
X *		(we have gotten the backspace in "_\bX" or "X\b_";
X *		we expect one more ordinary character, 
X *		which will put us back in state LN_UNDERLINE).
X *	LN_BO_X means we are one character after LN_BOLDFACE
X *		(we have gotten the 'X' in "X\bX").
X *	LN_BO_XB means we are one character after LN_BO_X
X *		(we have gotten the backspace in "X\bX";
X *		we expect one more 'X' which will put us back
X *		in LN_BOLDFACE).
X */
Xstatic int ln_state;		/* Currently in normal/underline/bold/etc mode? */
X#define	LN_NORMAL	0	/* Not in underline, boldface or whatever mode */
X#define	LN_UNDERLINE	1	/* In underline, need next char */
X#define	LN_UL_X		2	/* In underline, got char, need \b */
X#define	LN_UL_XB	3	/* In underline, got char & \b, need one more */
X#define	LN_BOLDFACE	4	/* In boldface, need next char */
X#define	LN_BO_X		5	/* In boldface, got char, need \b */
X#define	LN_BO_XB	6	/* In boldface, got char & \b, need same char */
X
Xextern int bs_mode;
Xextern int tabstop;
Xextern int linenums;
Xextern int sendctl;
Xextern int twiddle;
Xextern int auto_wrap, ignaw;
Xextern int bo_width, be_width;
Xextern int ul_width, ue_width;
Xextern int sc_width, sc_height;
X
X/*
X * Macros to manipulate the characters in the line buffer.
X * We encapsulate the manipulation here because of the complexity
X * of the "extended characters".  Each character is stored with
X * its low-order 8 bits in linebuf and the higher-order bits in
X * xlinebuf.  Normal characters are only 8 bits, but special
X * marker characters like BE_CHAR, etc. are wider than 8 bits.
X * In addition, the CARATBIT bit may be or-ed into any character
X * to cause it to be prefixed with a carat when printed.
X *
X *	STOC		Store a character near the current char position.
X *	MOVC		Move a character from one position to another.
X *	INC		Increment (or decrement) the current char position.
X *	READC		Read a character from near the current char position.
X *	SPACEUSED 	Returns the amount of space used in the buffer.
X *	SPACELEFT	Returns the amount of space remaining in the buffer.
X */
X#define	STOC(i,c)		{ linebuf[curr+(i)] = (c); \
X				  xlinebuf[curr+(i)] = ((c)>>8); }
X#define	MOVC(i,j)		{ linebuf[curr+(i)] = linebuf[curr+(j)]; \
X				  xlinebuf[curr+(i)] = xlinebuf[curr+(j)]; }
X#define	INC(i)			{ curr += (i); }
X#define	READC(i)		((linebuf[curr+(i)]&0377) | ((xlinebuf[curr+(i)]&0377)<<8))
X#define	SPACEUSED		(curr)
X#define	SPACELEFT		(sizeof(linebuf) - curr)
X
X
X/*
X * Rewind the line buffer.
X */
X	public void
Xprewind()
X{
X	curr = 0;
X	column = 0;
X	ln_state = LN_NORMAL;
X	is_null_line = 0;
X}
X
X/*
X * Insert the line number (of the given position) into the line buffer.
X */
X	public void
Xplinenum(pos)
X	POSITION pos;
X{
X	register int lno;
X	register int i;
X	register int n;
X	char buf[10];
X
X	/*
X	 * We display the line number at the start of each line
X	 * only if the -N option is set.
X	 */
X	if (linenums != 2)
X		return;
X
X	/*
X	 * Get the line number and put it in the current line.
X	 * {{ Note: since find_linenum calls forw_raw_line,
X	 *    it may seek in the input file, requiring the caller 
X	 *    of plinenum to re-seek if necessary. }}
X	 */
X	lno = find_linenum(pos);
X
X	sprintf(buf, "%6d", lno);
X	n = strlen(buf);
X	column += n;
X	for (i = 0;  i < n;  i++)
X	{
X		STOC(0, buf[i]);
X		INC(1);
X	}
X
X	/*
X	 * Append enough spaces to bring us to the next tab stop.
X	 * {{ We could avoid this at the cost of adding some
X	 *    complication to the tab stop logic in pappend(). }}
X	 */
X	do
X	{
X		STOC(0, ' ');
X		INC(1);
X		column++;
X	} while (column % tabstop);
X}
X
X/*
X * Append a character to the line buffer.
X * Expand tabs into spaces, handle underlining, boldfacing, etc.
X * Returns 0 if ok, 1 if couldn't fit in buffer.
X */
X
X#define	NEW_COLUMN(newcol)	if ((newcol) + ((ln_state)?ue_width:0) > sc_width) \
X					return (1); else column = (newcol)
X	public int
Xpappend(c)
X	int c;
X{
X	if (c == '\0')
X	{
X		/*
X		 * Terminate any special modes, if necessary.
X		 * Append a '\0' to the end of the line.
X		 */
X		switch (ln_state)
X		{
X		case LN_UL_X:
X			MOVC(0, -1);
X			STOC(-1, UE_CHAR);
X			INC(1);
X			break;
X		case LN_BO_X:
X			MOVC(0, -1);
X			STOC(-1, BE_CHAR);
X			INC(1);
X			break;
X		case LN_UL_XB:
X		case LN_UNDERLINE:
X			STOC(0, UE_CHAR);
X			INC(1);
X			break;
X		case LN_BO_XB:
X		case LN_BOLDFACE:
X			STOC(0, BE_CHAR);
X			INC(1);
X			break;
X		}
X		ln_state = LN_NORMAL;
X
X		if (column < sc_width || !auto_wrap || ignaw)
X		{
X			STOC(0, '\n');
X			INC(1);
X		}
X		STOC(0, '\0');
X		return (0);
X	}
X
X	if (SPACELEFT < 12)
X		/*
X		 * Almost out of room in the line buffer.
X		 * Don't take any chances.
X		 * {{ Linebuf is supposed to be big enough that this
X		 *    will never happen, but may need to be made 
X		 *    bigger for wide screens or lots of backspaces. }}
X		 */
X		return (1);
X
X	if (bs_mode == BS_SPECIAL)
X	{
X		/*
X		 * Advance the state machine.
X		 */
X		switch (ln_state)
X		{
X		case LN_NORMAL:
X			if (SPACEUSED <= 1 || READC(-1) != '\b')
X				break;
X
X			if (c == READC(-2))
X				goto enter_boldface;
X			if (c == '_' || READC(-2) == '_')
X				goto enter_underline;
X			INC(-2);
X			break;
X
Xenter_boldface:
X			/*
X			 * We have "X\bX" (including the current char).
X			 * Switch into boldface mode.
X			 */
X			if (column + bo_width + be_width + 1 >= sc_width)
X				/*
X				 * Not enough room left on the screen to 
X				 * enter and exit boldface mode.
X				 */
X				return (1);
X
X			if (bo_width > 0 && SPACEUSED > 2 && READC(-3) == ' ')
X			{
X				/*
X				 * Special case for magic cookie terminals:
X				 * if the previous char was a space, replace 
X				 * it with the "enter boldface" sequence.
X				 */
X				STOC(-3, BO_CHAR);
X				column += bo_width-1;
X			} else
X			{
X				MOVC(-1, -2);
X				STOC(-2, BO_CHAR);
X				column += bo_width;
X				INC(1);
X			}
X			goto ln_bo_xb_case;
X
Xenter_underline:
X			/*
X			 * We have either "_\bX" or "X\b_" (including
X			 * the current char).  Switch into underline mode.
X			 */
X			if (column + ul_width + ue_width + 1 >= sc_width)
X				/*
X				 * Not enough room left on the screen to 
X				 * enter and exit underline mode.
X				 */
X				return (1);
X
X			if (ul_width > 0 && SPACEUSED > 2 && READC(-3) == ' ')
X			{
X				/*
X				 * Special case for magic cookie terminals:
X				 * if the previous char was a space, replace 
X				 * it with the "enter underline" sequence.
X				 */
X				STOC(-3, UL_CHAR);
X				column += ul_width-1;
X			} else
X			{
X				MOVC(-1, -2);
X				STOC(-2, UL_CHAR);
X				column += ul_width;
X				INC(1);
X			}
X			goto ln_ul_xb_case;
X			/*NOTREACHED*/
X		case LN_UL_XB:
X			/*
X			 * Termination of a sequence "_\bX" or "X\b_".
X			 */
X			if (c != '_' && READC(-2) != '_' && c == READC(-2))
X			{
X				/*
X				 * We seem to have run on from underlining
X				 * into boldfacing - this is a nasty fix, but
X				 * until this whole routine is rewritten as a
X				 * real DFA, ...  well ...
X				 */
X				MOVC(0, -2);
X				STOC(-2, UE_CHAR);
X				STOC(-1, BO_CHAR);
X				INC(2); /* char & non-existent backspace */
X				ln_state = LN_BO_XB;
X				goto ln_bo_xb_case;
X			}
Xln_ul_xb_case:
X			if (c == '_')
X				c = READC(-2);
X			INC(-2);
X			ln_state = LN_UNDERLINE;
X			break;
X		case LN_BO_XB:
X			/*
X			 * Termination of a sequence "X\bX".
X			 */
X			if (c != READC(-2) && (c == '_' || READC(-2) == '_'))
X			{
X				/*
X				 * We seem to have run on from
X				 * boldfacing into underlining.
X				 */
X				MOVC(0, -2);
X				STOC(-2, BE_CHAR);
X				STOC(-1, UL_CHAR);
X				INC(2); /* char & non-existent backspace */
X				ln_state = LN_UL_XB;
X				goto ln_ul_xb_case;
X			}
Xln_bo_xb_case:
X			INC(-2);
X			ln_state = LN_BOLDFACE;
X			break;
X		case LN_UNDERLINE:
X			if (column + ue_width + bo_width + 1 + be_width >= sc_width)
X				/*
X				 * We have just barely enough room to 
X				 * exit underline mode and handle a possible
X				 * underline/boldface run on mixup.
X				 */
X				return (1);
X			ln_state = LN_UL_X;
X			break;
X		case LN_BOLDFACE:
X			if (c == '\b')
X			{
X				ln_state = LN_BO_XB;
X				break;
X			}
X			if (column + be_width + ul_width + 1 + ue_width >= sc_width)
X				/*
X				 * We have just barely enough room to 
X				 * exit underline mode and handle a possible
X				 * underline/boldface run on mixup.
X				 */
X				return (1);
X			ln_state = LN_BO_X;
X			break;
X		case LN_UL_X:
X			if (c == '\b')
X			{
X				ln_state = LN_UL_XB;
X			} else
X			{
X				/*
X				 * Exit underline mode.
X				 * We have to shuffle the chars a bit
X				 * to make this work.
X				 */
X				MOVC(0, -1);
X				STOC(-1, UE_CHAR);
X				column += ue_width;
X				if (ue_width > 0 && READC(0) == ' ')
X				{
X					/*
X					 * Another special case for magic
X					 * cookie terminals: if the next
X					 * char is a space, replace it
X					 * with the "exit underline" sequence.
X					 */
X					column--;
X				} else
X				{
X					INC(1);
X				}
X				ln_state = LN_NORMAL;
X			} 
X			break;
X		case LN_BO_X:
X			if (c == '\b')
X			{
X				ln_state = LN_BO_XB;
X			} else
X			{
X				/*
X				 * Exit boldface mode.
X				 * We have to shuffle the chars a bit
X				 * to make this work.
X				 */
X				MOVC(0, -1);
X				STOC(-1, BE_CHAR);
X				column += be_width;
X				if (be_width > 0 && READC(0) == ' ')
X				{
X					/*
X					 * Another special case for magic
X					 * cookie terminals: if the next
X					 * char is a space, replace it
X					 * with the "exit boldface" sequence.
X					 */
X					column--;
X				} else
X				{
X					INC(1);
X				}
X				ln_state = LN_NORMAL;
X			} 
X			break;
X		}
X	}
X	
X	if (c == '\t') 
X	{
X		/*
X		 * Expand a tab into spaces.
X		 */
X		do
X		{
X			STOC(0, ' ');
X			INC(1);
X			NEW_COLUMN(column+1);
X		} while ((column % tabstop) != 0);
X		return (0);
X	}
X
X	if (c == '\b')
X	{
X		if (bs_mode == BS_CONTROL)
X		{
X			/*
X			 * Treat backspace as a control char: output "^H".
X			 */
X			NEW_COLUMN(column+2);
X			STOC(0, ('H' | CARATBIT));
X			INC(1);
X		} else
X		{
X			/*
X			 * Output a real backspace.
X			 */
X			column--;
X			STOC(0, '\b');
X			INC(1);
X		}
X		return (0);
X	} 
X
X	if (control_char(c) && !sendctl)
X	{
X		/*
X		 * Put a "^X" into the buffer.
X		 * The CARATBIT bit is used to tell put_line() to prefix
X		 * the char with a ^.  We don't actually put the ^
X		 * in the buffer because we sometimes need to move
X		 * chars around, and such movement might separate 
X		 * the ^ from its following character.
X		 */
X		NEW_COLUMN(column+2);
X		STOC(0, (carat_char(c) | CARATBIT));
X		INC(1);
X		return (0);
X	}
X
X	/*
X	 * Ordinary character.  Just put it in the buffer.
X	 */
X	NEW_COLUMN(column+1);
X	STOC(0, c);
X	INC(1);
X	return (0);
X}
X
X/*
X * Get a character from the current line.
X */
X	public int
Xgline(i)
X	int i;
X{
X	if (is_null_line)
X	{
X		/*
X		 * If there is no current line, we pretend the line is
X		 * either "~" or "", depending on the "twiddle" flag.
X		 */
X		if (twiddle)
X			switch (i)
X			{
X			case 0:  return ('~');
X			case 1:  return ('\n');
X			}
X		return (0);
X	}
X
X	return ((linebuf[i]&0377) | ((xlinebuf[i]&0377)<<8));
X}
X
X/*
X * Indicate that there is no current line.
X */
X	public void
Xnull_line()
X{
X	is_null_line = 1;
X}
X
X/*
X * Analogous to forw_line(), but deals with "raw lines":
X * lines which are not split for screen width.
X * {{ This is supposed to be more efficient than forw_line(). }}
X */
X	public POSITION
Xforw_raw_line(curr_pos, linep)
X	POSITION curr_pos;
X	char **linep;
X{
X	register char *p;
X	register int c;
X	POSITION new_pos;
X
X	if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
X		(c = ch_forw_get()) == EOI)
X		return (NULL_POSITION);
X
X	p = linebuf;
X
X	for (;;)
X	{
X		if (c == '\n' || c == EOI)
X		{
X			new_pos = ch_tell();
X			break;
X		}
X		if (p >= &linebuf[sizeof(linebuf)-1])
X		{
X			/*
X			 * Overflowed the input buffer.
X			 * Pretend the line ended here.
X			 * {{ The line buffer is supposed to be big
X			 *    enough that this never happens. }}
X			 */
X			new_pos = ch_tell() - 1;
X			break;
X		}
X		*p++ = c;
X		c = ch_forw_get();
X	}
X	*p = '\0';
X	if (linep != NULL)
X		*linep = linebuf;
X	return (new_pos);
X}
X
X/*
X * Analogous to back_line(), but deals with "raw lines".
X * {{ This is supposed to be more efficient than back_line(). }}
X */
X	public POSITION
Xback_raw_line(curr_pos, linep)
X	POSITION curr_pos;
X	char **linep;
X{
X	register char *p;
X	register int c;
X	POSITION new_pos;
X
X	if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
X		ch_seek(curr_pos-1))
X		return (NULL_POSITION);
X
X	p = &linebuf[sizeof(linebuf)];
X	*--p = '\0';
X
X	for (;;)
X	{
X		c = ch_back_get();
X		if (c == '\n')
X		{
X			/*
X			 * This is the newline ending the previous line.
X			 * We have hit the beginning of the line.
X			 */
X			new_pos = ch_tell() + 1;
X			break;
X		}
X		if (c == EOI)
X		{
X			/*
X			 * We have hit the beginning of the file.
X			 * This must be the first line in the file.
X			 * This must, of course, be the beginning of the line.
X			 */
X			new_pos = (POSITION)0;
X			break;
X		}
X		if (p <= linebuf)
X		{
X			/*
X			 * Overflowed the input buffer.
X			 * Pretend the line ended here.
X			 */
X			new_pos = ch_tell() + 1;
X			break;
X		}
X		*--p = c;
X	}
X	if (linep != NULL)
X		*linep = p;
X	return (new_pos);
X}
END_OF_FILE

mark@unix386.Convergent.COM (Mark Nudelman) (09/15/89)

#! /bin/sh
# This is a shell archive.
# Remove anything before this line, then unpack it
# by saving it into a file and typing "sh file".

echo shar: Extracting \"linenum.c\"
sed "s/^X//" >'linenum.c' <<'END_OF_FILE'
X/*
X * Code to handle displaying line numbers.
X *
X * Finding the line number of a given file position is rather tricky.
X * We don't want to just start at the beginning of the file and
X * count newlines, because that is slow for large files (and also
X * wouldn't work if we couldn't get to the start of the file; e.g.
X * if input is a long pipe).
X *
X * So we use the function add_lnum to cache line numbers.
X * We try to be very clever and keep only the more interesting
X * line numbers when we run out of space in our table.  A line
X * number is more interesting than another when it is far from
X * other line numbers.   For example, we'd rather keep lines
X * 100,200,300 than 100,101,300.  200 is more interesting than
X * 101 because 101 can be derived very cheaply from 100, while
X * 200 is more expensive to derive from 100.
X *
X * The function currline() returns the line number of a given
X * position in the file.  As a side effect, it calls add_lnum
X * to cache the line number.  Therefore currline is occasionally
X * called to make sure we cache line numbers often enough.
X */
X
X#include "less.h"
X#include "position.h"
X
X/*
X * Structure to keep track of a line number and the associated file position.
X * A doubly-linked circular list of line numbers is kept ordered by line number.
X */
Xstruct linenum
X{
X	struct linenum *next;		/* Link to next in the list */
X	struct linenum *prev;		/* Line to previous in the list */
X	POSITION pos;			/* File position */
X	POSITION gap;			/* Gap between prev and next */
X	int line;			/* Line number */
X};
X/*
X * "gap" needs some explanation: the gap of any particular line number
X * is the distance between the previous one and the next one in the list.
X * ("Distance" means difference in file position.)  In other words, the
X * gap of a line number is the gap which would be introduced if this
X * line number were deleted.  It is used to decide which one to replace
X * when we have a new one to insert and the table is full.
X */
X
X#define	NPOOL	50			/* Size of line number pool */
X
X#define	LONGTIME	(2)		/* In seconds */
X
Xpublic int lnloop = 0;			/* Are we in the line num loop? */
X
Xstatic struct linenum anchor;		/* Anchor of the list */
Xstatic struct linenum *freelist;	/* Anchor of the unused entries */
Xstatic struct linenum pool[NPOOL];	/* The pool itself */
Xstatic struct linenum *spare;		/* We always keep one spare entry */
X
Xextern int linenums;
Xextern int sigs;
X
X/*
X * Initialize the line number structures.
X */
X	public void
Xclr_linenum()
X{
X	register struct linenum *p;
X
X	/*
X	 * Put all the entries on the free list.
X	 * Leave one for the "spare".
X	 */
X	for (p = pool;  p < &pool[NPOOL-2];  p++)
X		p->next = p+1;
X	pool[NPOOL-2].next = NULL;
X	freelist = pool;
X
X	spare = &pool[NPOOL-1];
X
X	/*
X	 * Initialize the anchor.
X	 */
X	anchor.next = anchor.prev = &anchor;
X	anchor.gap = 0;
X	anchor.pos = (POSITION)0;
X	anchor.line = 1;
X}
X
X/*
X * Calculate the gap for an entry.
X */
X	static void
Xcalcgap(p)
X	register struct linenum *p;
X{
X	/*
X	 * Don't bother to compute a gap for the anchor.
X	 * Also don't compute a gap for the last one in the list.
X	 * The gap for that last one should be considered infinite,
X	 * but we never look at it anyway.
X	 */
X	if (p == &anchor || p->next == &anchor)
X		return;
X	p->gap = p->next->pos - p->prev->pos;
X}
X
X/*
X * Add a new line number to the cache.
X * The specified position (pos) should be the file position of the
X * FIRST character in the specified line.
X */
X	public void
Xadd_lnum(lno, pos)
X	int lno;
X	POSITION pos;
X{
X	register struct linenum *p;
X	register struct linenum *new;
X	register struct linenum *nextp;
X	register struct linenum *prevp;
X	register POSITION mingap;
X
X	/*
X	 * Find the proper place in the list for the new one.
X	 * The entries are sorted by position.
X	 */
X	for (p = anchor.next;  p != &anchor && p->pos < pos;  p = p->next)
X		if (p->line == lno)
X			/* We already have this one. */
X			return;
X	nextp = p;
X	prevp = p->prev;
X
X	if (freelist != NULL)
X	{
X		/*
X		 * We still have free (unused) entries.
X		 * Use one of them.
X		 */
X		new = freelist;
X		freelist = freelist->next;
X	} else
X	{
X		/*
X		 * No free entries.
X		 * Use the "spare" entry.
X		 */
X		new = spare;
X		spare = NULL;
X	}
X
X	/*
X	 * Fill in the fields of the new entry,
X	 * and insert it into the proper place in the list.
X	 */
X	new->next = nextp;
X	new->prev = prevp;
X	new->pos = pos;
X	new->line = lno;
X
X	nextp->prev = new;
X	prevp->next = new;
X
X	/*
X	 * Recalculate gaps for the new entry and the neighboring entries.
X	 */
X	calcgap(new);
X	calcgap(nextp);
X	calcgap(prevp);
X
X	if (spare == NULL)
X	{
X		/*
X		 * We have used the spare entry.
X		 * Scan the list to find the one with the smallest
X		 * gap, take it out and make it the spare.
X		 * We should never remove the last one, so stop when
X		 * we get to p->next == &anchor.  This also avoids
X		 * looking at the gap of the last one, which is
X		 * not computed by calcgap.
X		 */
X		mingap = anchor.next->gap;
X		for (p = anchor.next;  p->next != &anchor;  p = p->next)
X		{
X			if (p->gap <= mingap)
X			{
X				spare = p;
X				mingap = p->gap;
X			}
X		}
X		spare->next->prev = spare->prev;
X		spare->prev->next = spare->next;
X	}
X}
X
X/*
X * If we get stuck in a long loop trying to figure out the
X * line number, print a message to tell the user what we're doing.
X */
X	static void
Xlongloopmessage()
X{
X	ierror("Calculating line numbers");
X	/*
X	 * Set the lnloop flag here, so if the user interrupts while
X	 * we are calculating line numbers, the signal handler will 
X	 * turn off line numbers (linenums=0).
X	 */
X	lnloop = 1;
X}
X
Xstatic int loopcount;
X#if GET_TIME
Xstatic long startime;
X#endif
X
X	static void
Xlongish()
X{
X#if GET_TIME
X	if (loopcount >= 0 && ++loopcount > 100)
X	{
X		loopcount = 0;
X		if (get_time() >= startime + LONGTIME)
X		{
X			longloopmessage();
X			loopcount = -1;
X		}
X	}
X#else
X	if (loopcount >= 0 && ++loopcount > LONGLOOP)
X	{
X		longloopmessage();
X		loopcount = -1;
X	}
X#endif
X}
X
X/*
X * Find the line number associated with a given position.
X * Return 0 if we can't figure it out.
X */
X	public int
Xfind_linenum(pos)
X	POSITION pos;
X{
X	register struct linenum *p;
X	register int lno;
X	POSITION cpos;
X
X	if (!linenums)
X		/*
X		 * We're not using line numbers.
X		 */
X		return (0);
X	if (pos == NULL_POSITION)
X		/*
X		 * Caller doesn't know what he's talking about.
X		 */
X		return (0);
X	if (pos == (POSITION)0)
X		/*
X		 * Beginning of file is always line number 1.
X		 */
X		return (1);
X
X	/*
X	 * Find the entry nearest to the position we want.
X	 */
X	for (p = anchor.next;  p != &anchor && p->pos < pos;  p = p->next)
X		continue;
X	if (p->pos == pos)
X		/* Found it exactly. */
X		return (p->line);
X
X	/*
X	 * This is the (possibly) time-consuming part.
X	 * We start at the line we just found and start
X	 * reading the file forward or backward till we
X	 * get to the place we want.
X	 *
X	 * First decide whether we should go forward from the 
X	 * previous one or backwards from the next one.
X	 * The decision is based on which way involves 
X	 * traversing fewer bytes in the file.
X	 */
X	flush();
X#if GET_TIME
X	startime = get_time();
X#endif
X	if (p == &anchor || pos - p->prev->pos < p->pos - pos)
X	{
X		/*
X		 * Go forward.
X		 */
X		p = p->prev;
X		if (ch_seek(p->pos))
X			return (0);
X		loopcount = 0;
X		for (lno = p->line, cpos = p->pos;  cpos < pos;  lno++)
X		{
X			/*
X			 * Allow a signal to abort this loop.
X			 */
X			cpos = forw_raw_line(cpos, (char **)NULL);
X			if (sigs || cpos == NULL_POSITION)
X				return (0);
X			longish();
X		}
X		lnloop = 0;
X		/*
X		 * We might as well cache it.
X		 */
X		add_lnum(lno, cpos);
X		/*
X		 * If the given position is not at the start of a line,
X		 * make sure we return the correct line number.
X		 */
X		if (cpos > pos)
X			lno--;
X	} else
X	{
X		/*
X		 * Go backward.
X		 */
X		if (ch_seek(p->pos))
X			return (0);
X		loopcount = 0;
X		for (lno = p->line, cpos = p->pos;  cpos > pos;  lno--)
X		{
X			/*
X			 * Allow a signal to abort this loop.
X			 */
X			cpos = back_raw_line(cpos, (char **)NULL);
X			if (sigs || cpos == NULL_POSITION)
X				return (0);
X			longish();
X		}
X		lnloop = 0;
X		/*
X		 * We might as well cache it.
X		 */
X		add_lnum(lno, cpos);
X	}
X
X	return (lno);
X}
X
X/*
X * Find the position of a given line number.
X * Return NULL_POSITION if we can't figure it out.
X */
X	public POSITION
Xfind_pos(lno)
X	int lno;
X{
X	register struct linenum *p;
X	POSITION cpos;
X	int clno;
X
X	if (lno <= 1)
X		/*
X		 * Line number 1 is beginning of file.
X		 */
X		return ((POSITION)0);
X
X	/*
X	 * Find the entry nearest to the line number we want.
X	 */
X	for (p = anchor.next;  p != &anchor && p->line < lno;  p = p->next)
X		continue;
X	if (p->line == lno)
X		/* Found it exactly. */
X		return (p->pos);
X
X	flush();
X	if (p == &anchor || lno - p->prev->line < p->line - lno)
X	{
X		/*
X		 * Go forward.
X		 */
X		p = p->prev;
X		if (ch_seek(p->pos))
X			return (NULL_POSITION);
X		for (clno = p->line, cpos = p->pos;  clno < lno;  clno++)
X		{
X			/*
X			 * Allow a signal to abort this loop.
X			 */
X			cpos = forw_raw_line(cpos, (char **)NULL);
X			if (sigs || cpos == NULL_POSITION)
X				return (NULL_POSITION);
X		}
X	} else
X	{
X		/*
X		 * Go backward.
X		 */
X		if (ch_seek(p->pos))
X			return (NULL_POSITION);
X		for (clno = p->line, cpos = p->pos;  clno > lno;  clno--)
X		{
X			/*
X			 * Allow a signal to abort this loop.
X			 */
X			cpos = back_raw_line(cpos, (char **)NULL);
X			if (sigs || cpos == NULL_POSITION)
X				return (NULL_POSITION);
X		}
X	}
X	/*
X	 * We might as well cache it.
X	 */
X	add_lnum(clno, cpos);
X	return (cpos);
X}
X
X/*
X * Return the line number of the "current" line.
X * The argument "where" tells which line is to be considered
X * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc).
X */
X	public int
Xcurrline(where)
X	int where;
X{
X	POSITION pos;
X	POSITION len;
X	int lnum;
X
X	pos = position(where);
X	len = ch_length();
X	if (pos == NULL_POSITION)
X		pos = len;
X	lnum = find_linenum(pos);
X	if (pos == len)
X		lnum--;
X	return (lnum);
X}
END_OF_FILE
echo shar: Extracting \"main.c\"
sed "s/^X//" >'main.c' <<'END_OF_FILE'
X/*
X * Entry point, initialization, miscellaneous routines.
X */
X
X#include "less.h"
X#include "position.h"
X
Xpublic int	ispipe;
Xpublic char *	first_cmd = NULL;
Xpublic char *	every_first_cmd = NULL;
Xpublic int	new_file;
Xpublic int	is_tty;
Xpublic char *	current_file = NULL;
Xpublic char *	previous_file = NULL;
Xpublic HANDLE	curr_handle;
Xpublic POSITION	initial_pos;
Xpublic int	any_display;
Xpublic int	scroll;
Xpublic int	ac;
Xpublic char **	av;
Xpublic int 	curr_ac;
Xpublic int	quitting;
X
Xextern int	file;
Xextern int	quit_at_eof;
Xextern int	hit_eof;
Xextern int	cbufs;
Xextern int	errmsgs;
Xextern int	screen_trashed;
X
X#if LOGFILE
Xpublic int	logfile = -1;
Xpublic int	force_logfile = 0;
Xpublic char *	namelogfile = NULL;
X#endif
X
X#if EDITOR
Xpublic char *	editor;
Xpublic char *	editproto;
X#endif
X
X#if TAGS
Xextern char *	tagfile;
Xextern char *	tagpattern;
Xextern int	tagoption;
X#endif
X
X
X/*
X * Edit a new file.
X * Filename "-" means standard input.
X * No filename means the "current" file, from the command line.
X */
X	public void
Xedit(filename)
X	register char *filename;
X{
X	register int f;
X	register char *m;
X	static int didpipe;
X
X	if (filename == NULL || *filename == '\0')
X	{
X		if (curr_ac >= ac)
X		{
X			error("No current file");
X			return;
X		}
X		filename = av[curr_ac];
X	}
X
X	if (strcmp(filename, "-") == 0)
X	{
X		/* 
X		 * Use standard input.
X		 */
X		if (didpipe)
X		{
X			error("Can view standard input only once");
X			return;
X		}
X		f = 0;
X	} else if ((m = bad_file(filename)) != NULL)
X	{
X		error(m);
X		free(m);
X		return;
X	} else if ((f = open(filename, 0)) < 0)
X	{
X		m = errno_message(filename);
X		error(m);
X		free(m);
X		return;
X	}
X
X	if (isatty(f))
X	{
X		/*
X		 * Not really necessary to call this an error,
X		 * but if the control terminal (for commands)
X		 * and the input file (for data) are the same,
X		 * we get weird results at best.
X		 */
X		error("Can't take input from a terminal (\"less -\\?\" for help)");
X		if (f > 0)
X			close(f);
X		return;
X	}
X
X#if LOGFILE
X	if (f == 0 && namelogfile != NULL && is_tty)
X		use_logfile();
X#endif
X
X	/*
X	 * We are now committed to using the new file.
X	 * Close the current input file and set up to use the new one.
X	 */
X	if (current_file != NULL)
X	{
X		store_pos(curr_handle, position(TOP));
X		lastmark();
X	}
X
X	curr_handle = get_handle(filename);
X	initial_pos = recall_pos(curr_handle);
X
X	previous_file = current_file;
X	current_file = get_filename(curr_handle);
X
X	if (file > 0)
X		close(file);
X	new_file = 1;
X	ispipe = (f == 0);
X	/*
X	 * {{ Would this make more sense? }}
X	 * ispipe = (lseek(f, (offset_t)0, 0) == BAD_LSEEK);
X	 */
X	if (ispipe)
X		didpipe = 1;
X	file = f;
X	ch_init(cbufs, 0);
X
X	if (every_first_cmd != NULL)
X		first_cmd = every_first_cmd;
X
X	if (is_tty)
X	{
X		int no_display = !any_display;
X		any_display = 1;
X		/*
X		 * Indicate there is nothing displayed yet.
X		 */
X		pos_clear();
X		clr_linenum();
X		if (no_display && errmsgs > 0)
X		{
X			/*
X			 * We displayed some messages on error output
X			 * (file descriptor 2; see error() function).
X			 * Before erasing the screen contents,
X			 * display the file name and wait for a keystroke.
X			 */
X			error(filename);
X		}
X	}
X}
X
X/*
X * Edit the next file in the command line list.
X */
X	public void
Xnext_file(n)
X	int n;
X{
X	if (curr_ac + n >= ac)
X	{
X		if (quit_at_eof && hit_eof)
X			quit();
X		error("No (N-th) next file");
X	} else
X		edit(av[curr_ac += n]);
X}
X
X/*
X * Edit the previous file in the command line list.
X */
X	public void
Xprev_file(n)
X	int n;
X{
X	if (curr_ac - n < 0)
X		error("No (N-th) previous file");
X	else
X		edit(av[curr_ac -= n]);
X}
X
X/*
X * Copy a file directly to standard output.
X * Used if standard output is not a tty.
X */
X	static void
Xcat_file()
X{
X	register int c;
X
X	while ((c = ch_forw_get()) != EOI)
X		putchr(c);
X	flush();
X}
X
X#if LOGFILE
X
X/*
X * If the user asked for a log file and our input file
X * is standard input, create the log file.  
X * We take care not to blindly overwrite an existing file.
X */
Xuse_logfile()
X{
X	register int exists;
X	register int answer;
X	register char *m;
X
X	end_logfile();
X
X	/*
X	 * {{ We could use access() here. }}
X	 */
X	exists = open(namelogfile, 0);
X	close(exists);
X	exists = (exists >= 0);
X
X	if (exists && !force_logfile)
X	{
X		static char w[] = "WARNING: log file exists: ";
X		m = ecalloc(sizeof(w) + strlen(namelogfile), sizeof(char));
X		strcpy(m, w);
X		strcat(m, namelogfile);
X		error(m);
X		free(m);
X		answer = 'X';	/* Ask the user what to do */
X	} else
X		answer = 'O';	/* Create the log file */
X
Xloop:
X	switch (answer)
X	{
X	case 'O': case 'o':
X		logfile = creat(namelogfile, 0644);
X		break;
X	case 'A': case 'a':
X		logfile = open(namelogfile, 1);
X		if (lseek(logfile, (offset_t)0, 2) == BAD_LSEEK)
X		{
X			close(logfile);
X			logfile = -1;
X		}
X		break;
X	case 'D': case 'd':
X		answer = 0;	/* Don't print an error message */
X		break;
X	case 'q':
X		quit();
X	default:
X		putstr("\n  Overwrite, Append, or Don't log? ");
X		screen_trashed = 1;
X		answer = getchr();
X		putstr("\n");
X		flush();
X		goto loop;
X	}
X
X	if (logfile < 0 && answer != 0)
X	{
X		m = ecalloc(strlen(namelogfile) + 20, sizeof(char));
X		sprintf(m, "Cannot write to \"%s\"", namelogfile);
X		error(m);
X		free(m);
X	}
X}
X
X#endif
X
X/*
X * Entry point.
X */
Xmain(argc, argv)
X	int argc;
X	char *argv[];
X{
X	register int i;
X	extern char *getenv();
X
X	/*
X	 * Process command line arguments and LESS environment arguments.
X	 * Command line arguments override environment arguments.
X	 */
X	init_prompt();
X	init_option();
X	scan_option(getenv("LESS"));
X
X#define	isoptstring(s)	(((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0')
X	argv++;
X	while (--argc > 0 && (isoptstring(argv[0]) || isoptpending()))
X		scan_option(*argv++);
X#undef isoptstring
X
X	if (isoptpending())
X	{
X		/*
X		 * Last command line option was a flag requiring a
X		 * following string, but there was no following string.
X		 */
X		nopendopt();
X		exit(0);
X	}
X
X#if USERFILE
X	/*
X	 * Try to use the lesskey file "$HOME/.less".
X	 */
X	add_hometable();
X#endif
X#if EDITOR
X	editor = getenv("EDITOR");
X	if (editor == NULL || *editor == '\0')
X		editor = EDIT_PGM;
X	editproto = getenv("EDITPROTO");
X	if (editproto == NULL || *editproto == '\0')
X		editproto = "%E ?lm+%lm. %f";
X#endif
X
X	/*
X	 * Set up list of files to be examined.
X	 */
X	ac = argc;
X	av = argv;
X	curr_ac = 0;
X
X	/*
X	 * Set up terminal, etc.
X	 */
X	is_tty = isatty(1);
X	if (!is_tty)
X	{
X		/*
X		 * Output is not a tty.
X		 * Just copy the input file(s) to output.
X		 */
X		if (ac < 1)
X		{
X			edit("-");
X			cat_file();
X		} else
X		{
X			do
X			{
X				edit((char *)NULL);
X				if (file >= 0)
X					cat_file();
X			} while (++curr_ac < ac);
X		}
X		exit(0);
X	}
X
X	/*
X	 * Call save_handle with all the command line filenames,
X	 * just to avoid copying them to calloc'ed space later
X	 * when we call get_handle() from edit().
X	 */
X	for (i = 0;  i < ac;  i++)
X		save_handle(av[i]);
X
X	init_mark();
X	raw_mode(1);
X	get_term();
X	open_getchr();
X	init();
X
X	init_signals(1);
X
X	/*
X	 * Select the first file to examine.
X	 */
X#if TAGS
X	if (tagoption)
X	{
X		/*
X		 * A -t option was given.
X		 * Verify that no filenames were also given.
X		 * Edit the file selected by the "tags" search,
X		 * and search for the proper line in the file.
X		 */
X		if (ac > 0)
X		{
X			error("No filenames allowed with -t option");
X			quit();
X		}
X		if (tagfile == NULL)
X			quit();
X		edit(tagfile);
X		if (file < 0)
X			quit();
X		if (tagsearch())
X			quit();
X	} else
X#endif
X	if (ac < 1)
X		edit("-");	/* Standard input */
X	else 
X	{
X		/*
X		 * Try all the files named as command arguments.
X		 * We are simply looking for one which can be
X		 * opened without error.
X		 */
X		do
X		{
X			edit((char *)NULL);
X		} while (file < 0 && ++curr_ac < ac);
X	}
X
X	if (file >= 0)
X		commands();
X	quit();
X	/*NOTREACHED*/
X}
X
X/*
X * Copy a string, truncating to the specified length if necessary.
X * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
X */
X	public void
Xstrtcpy(to, from, len)
X	char *to;
X	char *from;
X	unsigned int len;
X{
X	strncpy(to, from, len);
X	to[len-1] = '\0';
X}
X
X/*
X * Copy a string to a "safe" place
X * (that is, to a buffer allocated by calloc).
X */
X	public char *
Xsave(s)
X	char *s;
X{
X	register char *p;
X
X	p = (char *) ecalloc(strlen(s)+1, sizeof(char));
X	strcpy(p, s);
X	return (p);
X}
X
X	public char *
Xecalloc(count, size)
X	int count;
X	unsigned int size;
X{
X	register char *p;
X
X	p = calloc(count, size);
X	if (p != NULL)
X		return (p);
X	error("Cannot allocate memory");
X	quit();
X	/*NOTREACHED*/
X}
X
X/*
X * Skip leading spaces in a string.
X */
X	public char *
Xskipsp(s)
X	register char *s;
X{
X	while (*s == ' ' || *s == '\t')	
X		s++;
X	return (s);
X}
X
X/*
X * Exit the program.
X */
X	public void
Xquit()
X{
X	/*
X	 * Put cursor at bottom left corner, clear the line,
X	 * reset the terminal modes, and exit.
X	 */
X	quitting = 1;
X#if LOGFILE
X	end_logfile();
X#endif
X	lower_left();
X	clear_eol();
X	deinit();
X	flush();
X	raw_mode(0);
X	exit(0);
X}
END_OF_FILE
echo shar: Extracting \"option.c\"
sed "s/^X//" >'option.c' <<'END_OF_FILE'
X/*
X * Process command line options.
X *
X * Each option is a single letter which controls a program variable.
X * The options have defaults which may be changed via
X * the command line option, toggled via the "-" command, 
X * or queried via the "_" command.
X */
X
X#include "less.h"
X#include "option.h"
X
Xstatic struct option *pendopt;
Xpublic int plusoption;
X
Xstatic char *propt();
Xstatic char *optstring();
X
Xextern int screen_trashed;
Xextern char *first_cmd;
Xextern char *every_first_cmd;
X
X/* 
X * Scan an argument (either from the command line or from the 
X * LESS environment variable) and process it.
X */
X	public void
Xscan_option(s)
X	char *s;
X{
X	register struct option *o;
X	register int c;
X	char *str;
X	int set_default;
X	char message[80];
X
X	if (s == NULL)
X		return;
X
X	/*
X	 * If we have a pending string-valued option, handle it now.
X	 * This happens if the previous option was, for example, "-P"
X	 * without a following string.  In that case, the current
X	 * option is simply the string for the previous option.
X	 */
X	if (pendopt != NULL)
X	{
X		(*pendopt->ofunc)(INIT, s);
X		pendopt = NULL;
X		return;
X	}
X
X	set_default = 0;
X
X	while (*s != '\0')
X	{
X		/*
X		 * Check some special cases first.
X		 */
X		switch (c = *s++)
X		{
X		case ' ':
X		case '\t':
X		case END_OPTION_STRING:
X			continue;
X		case '-':
X			/*
X			 * "-+" means set these options back to their defaults.
X			 * (They may have been set otherwise by previous 
X			 * options.)
X			 */
X			if (set_default = (*s == '+'))
X				s++;
X			continue;
X		case '+':
X			/*
X			 * An option prefixed by a "+" becomes the "first_cmd"
X			 * string, which is interpreted as less commands 
X			 * processed at the start of the first input file.
X			 * "++" means process the commands at the start of
X			 * EVERY input file.
X			 */
X			plusoption = 1;
X			if (*s == '+')
X				every_first_cmd = save(++s);
X			first_cmd = s;
X			s = optstring(s, c);
X			continue;
X		case '0':  case '1':  case '2':  case '3':  case '4':
X		case '5':  case '6':  case '7':  case '8':  case '9':
X			/*
X			 * Special "more" compatibility form "-<number>"
X			 * instead of -z<number> to set the scrolling 
X			 * window size.
X			 */
X			s--;
X			c = 'z';
X			break;
X		}
X
X		/*
X		 * Not a special case.
X		 * Look up the option letter in the option table.
X		 */
X		o = findopt(c);
X		if (o == NULL)
X		{
X			sprintf(message, 
X				"There is no %s flag (\"less -\\?\" for help)",
X				propt(c));
X			error(message);
X			exit(1);
X		}
X
X		switch (o->otype & OTYPE)
X		{
X		case BOOL:
X			if (set_default)
X				*(o->ovar) = o->odefault;
X			else
X				*(o->ovar) = ! o->odefault;
X			break;
X		case TRIPLE:
X			if (set_default)
X				*(o->ovar) = o->odefault;
X			else if (o->oletter == c)
X				*(o->ovar) = (o->odefault == 1) ? 0 : 1;
X			else
X				*(o->ovar) = (o->odefault == 2) ? 0 : 2;
X			break;
X		case STRING:
X			if (*s == '\0')
X			{
X				/*
X				 * Set pendopt and return.
X				 * We will get the string next time
X				 * scan_option is called.
X				 */
X				pendopt = o;
X				return;
X			}
X			/*
X			 * Don't do anything here.
X			 * All processing of STRING options is done by 
X			 * the handling function.
X			 */
X			str = s;
X			s = optstring(s, c);
X			break;
X		case NUMBER:
X			*(o->ovar) = getnum(&s, c);
X			break;
X		}
X		/*
X		 * If the option has a handling function, call it.
X		 */
X		if (o->ofunc != NULL)
X			(*o->ofunc)(INIT, str);
X	}
X}
X
X/*
X * Toggle command line flags from within the program.
X * Used by the "-" and "_" commands.
X * If do_toggle is zero, just report the current setting, without changing it.
X */
X	public void
Xtoggle_option(c, s, do_toggle)
X	int c;
X	char *s;
X	int do_toggle;
X{
X	register struct option *o;
X	int num;
X	char message[100];
X
X	/*
X	 * Look up the option letter in the option table.
X	 */
X	o = findopt(c);
X	if (o == NULL)
X	{
X		sprintf(message, "There is no %s flag", propt(c));
X		error(message);
X		return;
X	}
X
X	/*
X	 * Check for something which appears to be a do_toggle
X	 * (because the "-" command was used), but really is not.
X	 * This could be a string option with no string, or
X	 * a number option with no number.
X	 */
X	switch (o->otype & OTYPE)
X	{
X	case STRING:
X		if (*s == '\0')
X			do_toggle = 0;
X		break;
X	case NUMBER:
X		num = getnum(&s, '\0');
X		if (num < 0)
X			do_toggle = 0;
X		break;
X	}
X
X	/*
X	 * Now actually toggle (change) the variable.
X	 */
X	if (do_toggle)
X	{
X		if (o->otype & NO_TOGGLE)
X		{
X			sprintf(message, "Cannot change the %s flag", propt(c));
X			error(message);
X			return;
X		} 
X
X		switch (o->otype & OTYPE)
X		{
X		case BOOL:
X			/*
X			 * Boolean: just negate.
X			 */
X			*(o->ovar) = ! *(o->ovar);
X			break;
X		case TRIPLE:
X			/*
X			 * Triple:
X			 *	If user gave the lower case letter, then switch 
X			 *	to 1 unless already 1, in which case make it 0.
X			 *	If user gave the upper case letter, then switch
X			 *	to 2 unless already 2, in which case make it 0.
X			 */
X			if (o->oletter == c)
X				*(o->ovar) = (*(o->ovar) == 1) ? 0 : 1;
X			else
X				*(o->ovar) = (*(o->ovar) == 2) ? 0 : 2;
X			break;
X		case STRING:
X			/*
X			 * String: don't do anything here.
X			 *	The handling function will do everything.
X			 */
X			break;
X		case NUMBER:
X			/*
X			 * Number: set the variable to the given number.
X			 */
X			*(o->ovar) = num;
X			break;
X		}
X	}
X
X	/*
X	 * Call the handling function for any special action 
X	 * specific to this option.
X	 */
X	if (o->ofunc != NULL)
X		(*o->ofunc)(do_toggle ? TOGGLE : QUERY, s);
X
X	/*
X	 * Print a message describing the new setting.
X	 */
X	switch (o->otype & OTYPE)
X	{
X	case BOOL:
X	case TRIPLE:
X		/*
X		 * Print the odesc message.
X		 */
X		error(o->odesc[*(o->ovar)]);
X		break;
X	case NUMBER:
X		/*
X		 * The odesc message has a %d for the value of the variable.
X		 */
X		sprintf(message, o->odesc[0], *(o->ovar));
X		error(message);
X		break;
X	case STRING:
X		/*
X		 * Message was already printed by the handling function.
X		 */
X		break;
X	}
X
X	if (do_toggle && (o->otype & REPAINT))
X		screen_trashed = 1;
X}
X
X/*
X * Return a string suitable for printing as the "name" of an option.
X * For example, if the option letter is 'x', just return "-x".
X */
X	static char *
Xpropt(c)
X	int c;
X{
X	static char buf[4];
X
X	if (control_char(c))
X		sprintf(buf, "-^%c", carat_char(c));
X	else
X		sprintf(buf, "-%c", c);
X	return (buf);
X}
X
X/*
X * Determine if an option is a single character option (BOOL or TRIPLE),
X * or if it a multi-character option (NUMBER).
X */
X	public int
Xsingle_char_option(c)
X	int c;
X{
X	register struct option *o;
X
X	o = findopt(c);
X	if (o == NULL)
X		return (1);
X	return (o->otype & (BOOL|TRIPLE|NOVAR));
X}
X
X/*
X * Return the prompt to be used for a given option letter.
X * Only string valued options have prompts.
X */
X	public char *
Xopt_prompt(c)
X	int c;
X{
X	register struct option *o;
X
X	o = findopt(c);
X	if (o == NULL || (o->otype & STRING) == 0)
X		return (NULL);
X	return (o->odesc[0]);
X}
X
X/*
X * Return whether or not there is a string option pending;
X * that is, if the previous option was a string-valued option letter 
X * (like -P) without a following string.
X * In that case, the current option is taken to be the string for
X * the previous option.
X */
X	public int
Xisoptpending()
X{
X	return (pendopt != NULL);
X}
X
X/*
X * Print error message about missing string.
X */
X	static void
Xnostring(c)
X	int c;
X{
X	char message[80];
X
X	sprintf(message, "String is required after %s", propt(c));
X	error(message);
X}
X
X/*
X * Print error message if a STRING type option is not followed by a string.
X */
X	public void
Xnopendopt()
X{
X	nostring(pendopt->oletter);
X}
X
X/*
X * Scan to end of string or to an END_OPTION_STRING character.
X * In the latter case, replace the char with a null char.
X * Return a pointer to the remainder of the string, if any.
X */
X	static char *
Xoptstring(s, c)
X	char *s;
X	int c;
X{
X	register char *p;
X
X	if (*s == '\0')
X	{
X		nostring(c);
X		exit(1);
X	}
X	for (p = s;  *p != '\0';  p++)
X		if (*p == END_OPTION_STRING)
X		{
X			*p = '\0';
X			return (p+1);
X		}
X	return (p);
X}
X
X/*
X * Translate a string into a number.
X * Like atoi(), but takes a pointer to a char *, and updates
X * the char * to point after the translated number.
X */
X	public int
Xgetnum(sp, c)
X	char **sp;
X	int c;
X{
X	register char *s;
X	register int n;
X	char message[80];
X
X	s = skipsp(*sp);
X	if (*s < '0' || *s > '9')
X	{
X		if (c == '\0')
X			return (-1);
X		sprintf(message, "Number is required after %s", propt(c));
X		error(message);
X		exit(1);
X	}
X
X	n = 0;
X	while (*s >= '0' && *s <= '9')
X		n = 10 * n + *s++ - '0';
X	*sp = s;
X	return (n);
X}
END_OF_FILE
echo shar: Extracting \"optfunc.c\"
sed "s/^X//" >'optfunc.c' <<'END_OF_FILE'
X/*
X * Handling functions for command line options.
X *
X * Most options are handled by the generic code in option.c.
X * But all string options, and a few non-string options, require
X * special handling specific to the particular option.
X * This special processing is done by the "handling functions" in this file.
X *
X * Each handling function is passed a "type" and, if it is a string
X * option, the string which should be "assigned" to the option.
X * The type may be one of:
X *	INIT	The option is being initialized from the command line.
X *	TOGGLE	The option is being changed from within the program.
X *	QUERY	The setting of the option is merely being queried.
X */
X
X#include "less.h"
X#include "option.h"
X
Xextern int nbufs;
Xextern int ispipe;
Xextern int cbufs;
Xextern int pr_type;
Xextern int seven_bit;
Xextern int nohelp;
Xextern char *prproto[];
Xextern char *eqproto;
X#if LOGFILE
Xextern char *namelogfile;
Xextern int force_logfile;
Xextern int logfile;
X#endif
X#if TAGS
Xpublic int tagoption = 0;
Xextern char *tagfile;
Xextern char *tagpattern;
X#endif
X
X
X#if LOGFILE
X/*
X * Handler for -l option.
X */
X	public void
Xopt_l(type, s)
X	int type;
X	char *s;
X{
X	char *m;
X
X	switch (type)
X	{
X	case INIT:
X		namelogfile = s;
X		break;
X	case TOGGLE:
X		if (!ispipe)
X		{
X			error("Input is not a pipe");
X			return;
X		}
X		if (logfile >= 0)
X		{
X			error("Log file is already in use");
X			return;
X		}
X		namelogfile = save(skipsp(s));
X		use_logfile();
X		sync_logfile();
X		break;
X	case QUERY:
X		if (logfile < 0)
X			error("No log file");
X		else
X		{
X			static char lf[] = "log file \"";
X			m = ecalloc(sizeof(lf) + strlen(namelogfile) + 2, sizeof(char));
X			strcpy(m, lf);
X			strcat(m, namelogfile);
X			strcat(m, "\"");
X			error(m);
X			free(m);
X		}
X		break;
X	}
X}
X
X/*
X * Handler for -L option.
X */
X	public void
Xopt__L(type, s)
X	int type;
X	char *s;
X{
X	force_logfile = 1;
X	opt_l(type, s);
X}
X#endif
X
X#if USERFILE
X	public void
Xopt_k(type, s)
X	int type;
X	char *s;
X{
X	char *message;
X	static char MSG[] = "Cannot use lesskey file: ";
X
X	switch (type)
X	{
X	case INIT:
X		if (add_cmdtable(s) == 0)
X			return;
X		message = (char *) ecalloc(strlen(s) + sizeof(MSG) + 1, 
X						sizeof(char));
X		strcpy(message, MSG);
X		strcat(message, s);
X		error(message);
X		free(message);
X		break;
X	case QUERY:
X	case TOGGLE:
X		error("Cannot query -k option");
X		break;
X	}
X}
X#endif
X
X#if TAGS
X/*
X * Handler for -t option.
X */
X	public void
Xopt_t(type, s)
X	int type;
X	char *s;
X{
X	switch (type)
X	{
X	case INIT:
X		tagoption = 1;
X		findtag(s);
X		break;
X	case TOGGLE:
X		findtag(skipsp(s));
X		if (tagfile != NULL)
X		{
X			edit(tagfile);
X			(void) tagsearch();
X		}
X		break;
X	case QUERY:
X		error("Tag is required after -t");
X		break;
X	}
X}
X#endif
X
X/*
X * Handler for -P option.
X */
X	public void
Xopt_P(type, s)
X	int type;
X	register char *s;
X{
X	register char **proto;
X
X	switch (type)
X	{
X	case INIT:
X	case TOGGLE:
X		/*
X		 * Figure out which prototype string should be changed.
X		 */
X		switch (*s)
X		{
X		case 'm':  proto = &prproto[PR_MEDIUM];	s++;	break;
X		case 'M':  proto = &prproto[PR_LONG];	s++;	break;
X		case '=':  proto = &eqproto;		s++;	break;
X		default:   proto = &prproto[pr_type];		break;
X		}
X		free(*proto);
X		*proto = save(s);
X		break;
X	case QUERY:
X		error(prproto[pr_type]);
X		break;
X	}
X}
X
X/*
X * Handler for the -b option.
X */
X	public void
Xopt_b(type)
X	int type;
X{
X	switch (type)
X	{
X	case TOGGLE:
X		/*
X		 * Allocate the new number of buffers.
X		 */
X		ch_init(cbufs, 1);
X		break;
X	case QUERY:
X	case INIT:
X		break;
X	}
X}
X
X/*
X * Handler for the -g option.
X */
X	public void
Xopt_g(type)
X	int type;
X{
X	switch (type)
X	{
X	case TOGGLE:
X		/*
X		 * Flush the buffers, since the buffered data may
X		 * be bad (if we are switching from 7 bits to 8 bits,
X		 * the eighth bit has been stripped from the buffered data).
X		 */
X		if (ispipe)
X		{
X			error("Can't change -g on a pipe");
X			seven_bit = !seven_bit;
X			break;
X		}
X		ch_init(0, 0);
X		break;
X	case INIT:
X	case QUERY:
X		break;
X	}
X}
X
X/*
X * "-?" means display a help message.
X * If from the command line, exit immediately.
X */
X	public void
Xopt_query(type)
X	int type;
X{
X	if (nohelp)
X		return;
X	switch (type)
X	{
X	case QUERY:
X	case TOGGLE:
X		error("Use \"h\" for help");
X		break;
X	case INIT:
X		raw_mode(1);
X		init();
X		help();
X		quit();
X		/*NOTREACHED*/
X	}
X}
END_OF_FILE
echo shar: Extracting \"opttbl.c\"
sed "s/^X//" >'opttbl.c' <<'END_OF_FILE'
X/*
X * The option table.
X */
X
X#include "less.h"
X#include "option.h"
X
X#define	toupper(c)	((c)-'a'+'A')
X
X/*
X * Variables controlled by command line options.
X */
Xpublic int seven_bit;		/* Force seven-bit characters? */
Xpublic int quiet;		/* Should we suppress the audible bell? */
Xpublic int how_search;		/* Where should forward searches start? */
Xpublic int top_scroll;		/* Repaint screen from top?
X				   (alternative is scroll from bottom) */
Xpublic int pr_type;		/* Type of prompt (short, medium, long) */
Xpublic int bs_mode;		/* How to process backspaces */
Xpublic int know_dumb;		/* Don't complain about dumb terminals */
Xpublic int quit_at_eof;		/* Quit after hitting end of file twice */
Xpublic int squeeze;		/* Squeeze multiple blank lines into one */
Xpublic int tabstop;		/* Tab settings */
Xpublic int back_scroll;		/* Repaint screen on backwards movement */
Xpublic int twiddle;		/* Display "~" for lines after EOF */
Xpublic int caseless;		/* Do "caseless" searches */
Xpublic int linenums;		/* Use line numbers */
Xpublic int cbufs;		/* Current number of buffers */
Xpublic int autobuf;		/* Automatically allocate buffers as needed */
Xpublic int nohelp;		/* Disable the HELP command */
Xpublic int sendctl;		/* Send control chars to screen untranslated */
Xpublic int force_open;		/* Open the file even if not regular file */
Xpublic int swindow;
X
X/*
X * Table of all options and their semantics.
X */
Xstatic struct option option[] =
X{
X	{ 'a', TRIPLE, 0, &how_search, NULL,
X		"Forward search starts at second REAL line displayed",
X		"Forward search starts at bottom of screen",
X		"Forward search starts at second SCREEN line displayed"
X	},
X	{ 'b', NUMBER, 10, &cbufs, opt_b, 
X		"%d buffers",
X		NULL, NULL
X	},
X	{ 'B', BOOL, 1, &autobuf, NULL,
X		"Don't automatically allocate buffers",
X		"Automatically allocate buffers when needed",
X		NULL
X	},
X	{ 'c', TRIPLE, 0, &top_scroll, NULL,
X		"Repaint by scrolling from bottom of screen",
X		"Repaint by clearing each line",
X		"Repaint by painting from top of screen"
X	},
X	{ 'd', BOOL|NO_TOGGLE, 0, &know_dumb, NULL,
X		"Assume intelligent terminal",
X		"Assume dumb terminal",
X		NULL
X	},
X	{ 'e', TRIPLE, 0, &quit_at_eof, NULL,
X		"Don't quit at end-of-file",
X		"Quit at end-of-file",
X		"Quit immediately at end-of-file"
X	},
X	{ 'f', BOOL, 0, &force_open, NULL,
X		"Open only regular files",
X		"Open even non-regular files",
X		NULL
X	},
X	{ 'g', BOOL, 0, &seven_bit, opt_g,
X		"Use eight bit characters",
X		"Use seven bit characters",
X		NULL
X	},
X	{ 'h', NUMBER, -1, &back_scroll, NULL,
X		"Backwards scroll limit is %d lines",
X		NULL, NULL
X	},
X	{ 'H', BOOL|NO_TOGGLE, 0, &nohelp, NULL,
X		"Allow help command",
X		"Don't allow help command",
X		NULL
X	},
X	{ 'i', BOOL, 0, &caseless, NULL,
X		"Case is significant in searches",
X		"Ignore case in searches",
X		NULL
X	},
X#if USERFILE
X	{ 'k', STRING|NO_TOGGLE, 0, NULL, opt_k,
X		NULL, NULL, NULL
X	},
X#endif
X#if LOGFILE
X	{ 'l', STRING, 0, NULL, opt_l,
X		"log file: ", NULL, NULL
X	},
X	{ 'L', STRING, 0, NULL, opt__L,
X		"Log file: ", NULL, NULL
X	},
X#endif
X	{ 'm', TRIPLE, 0, &pr_type, NULL,
X		"Short prompt",
X		"Medium prompt",
X		"Long prompt"
X	},
X	{ 'n', TRIPLE|REPAINT, 1, &linenums, NULL,
X		"Don't use line numbers",
X		"Use line numbers",
X		"Constantly display line numbers"
X	},
X	{ 'P', STRING, 0, NULL, opt_P,
X		"prompt: ", NULL, NULL
X	},
X	{ 'q', TRIPLE, 0, &quiet, NULL,
X		"Ring the bell for errors AND at eof/bof",
X		"Ring the bell for errors but not at eof/bof",
X		"Never ring the bell"
X	},
X	{ 'r', BOOL|REPAINT, 0, &sendctl, NULL,
X		"Control characters are translated",
X		"Control characters displayed directly",
X		NULL
X	},
X	{ 's', BOOL|REPAINT, 0, &squeeze, NULL,
X		"Don't squeeze multiple blank lines",
X		"Squeeze multiple blank lines",
X		NULL
X	},
X#if TAGS
X	{ 't', STRING, 0, NULL, opt_t,
X		"tag: ", NULL, NULL
X	},
X#endif
X	{ 'u', TRIPLE|REPAINT, 0, &bs_mode, NULL,
X		"Underlined text displayed in underline mode",
X		"Backspaces cause overstrike",
X		"Backspaces print as ^H"
X	},
X	{ 'w', BOOL|REPAINT, 1, &twiddle, NULL,
X		"Display nothing for lines after end-of-file",
X		"Display ~ for lines after end-of-file",
X		NULL
X	},
X	{ 'x', NUMBER|REPAINT, 8, &tabstop, NULL,
X		"Tab stops every %d spaces", 
X		NULL, NULL
X		
X	},
X	{ 'z', NUMBER, -1, &swindow, NULL,
X		"Scroll window size is %d lines",
X		NULL, NULL
X	},
X	{ '?', NOVAR, 0, NULL, opt_query,
X		NULL, NULL, NULL
X	},
X	{ '\0' }
X};
X
X
X/*
X * Initialize each option to its default value.
X */
X	public void
Xinit_option()
X{
X	register struct option *o;
X
X	for (o = option;  o->oletter != '\0';  o++)
X	{
X		/*
X		 * Set each variable to its default.
X		 */
X		if (o->ovar != NULL)
X			*(o->ovar) = o->odefault;
X	}
X}
X
X/*
X * Find an option in the option table.
X */
X	public struct option *
Xfindopt(c)
X	int c;
X{
X	register struct option *o;
X
X	for (o = option;  o->oletter != '\0';  o++)
X	{
X		if (o->oletter == c)
X			return (o);
X		if ((o->otype & TRIPLE) && toupper(o->oletter) == c)
X			return (o);
X	}
X	return (NULL);
X}
END_OF_FILE
echo shar: Extracting \"os.c\"
sed "s/^X//" >'os.c' <<'END_OF_FILE'
X/*
X * Operating system dependent routines.
X *
X * Most of the stuff in here is based on Unix, but an attempt
X * has been made to make things work on other operating systems.
X * This will sometimes result in a loss of functionality, unless
X * someone rewrites code specifically for the new operating system.
X *
X * The makefile provides defines to decide whether various
X * Unix features are present.
X */
X
X#include <stdio.h>
X#include <signal.h>
X#include <setjmp.h>
X#include "less.h"
X
Xextern char *getenv();
X
Xpublic int reading;
X
Xextern int screen_trashed;
Xextern int force_open;
Xextern char *current_file;
Xextern char *previous_file;
X
Xstatic jmp_buf read_label;
X
X/*
X * Pass the specified command to a shell to be executed.
X * Like plain "system()", but handles resetting terminal modes, etc.
X */
X	public void
Xlsystem(cmd)
X	char *cmd;
X{
X	register int inp;
X	register char *shell;
X	register char *p;
X
X	/*
X	 * Print the command which is to be executed,
X	 * unless the command starts with a "-".
X	 */
X	if (cmd[0] == '-')
X		cmd++;
X	else
X	{
X		lower_left();
X		clear_eol();
X		putstr("!");
X		putstr(cmd);
X		putstr("\n");
X	}
X
X	/*
X	 * De-initialize the terminal and take out of raw mode.
X	 */
X	deinit();
X	flush();
X	raw_mode(0);
X
X	/*
X	 * Restore signals to their defaults.
X	 */
X	init_signals(0);
X
X	/*
X	 * Force standard input to be the terminal, "/dev/tty",
X	 * even if less's standard input is coming from a pipe.
X	 */
X	inp = dup(0);
X	close(0);
X	if (open("/dev/tty", 0) < 0)
X		dup(inp);
X
X	/*
X	 * Pass the command to the system to be executed.
X	 * If we have a SHELL environment variable, use
X	 * <$SHELL -c "command"> instead of just <command>.
X	 * If the command is empty, just invoke a shell.
X	 */
X	p = NULL;
X	if ((shell = getenv("SHELL")) != NULL && *shell != '\0')
X	{
X		if (*cmd == '\0')
X			p = save(shell);
X		else
X		{
X			p = ecalloc(strlen(shell) + strlen(cmd) + 7, sizeof(char));
X			sprintf(p, "%s -c \"%s\"", shell, cmd);
X		}
X	}
X	if (p == NULL)
X		p = save("sh");
X
X	system(p);
X	free(p);
X
X	/*
X	 * Restore standard input, reset signals, raw mode, etc.
X	 */
X	close(0);
X	dup(inp);
X	close(inp);
X
X	init_signals(1);
X	raw_mode(1);
X	init();
X	screen_trashed = 1;
X#if defined(SIGWINCH) || defined(SIGWIND)
X	/*
X	 * Since we were ignoring window change signals while we executed
X	 * the system command, we must assume the window changed.
X	 * Warning: this leaves a signal pending (in "sigs"),
X	 * so psignals() should be called soon after lsystem().
X	 */
X	winch();
X#endif
X}
X
X/*
X * Like read() system call, but is deliberately interruptible.
X * A call to intread() from a signal handler will interrupt
X * any pending iread().
X */
X	public int
Xiread(fd, buf, len)
X	int fd;
X	char *buf;
X	int len;
X{
X	register int n;
X
X	if (setjmp(read_label))
X	{
X		/*
X		 * We jumped here from intread.
X		 */
X		reading = 0;
X#if SIGSETMASK
X		sigsetmask(0);
X#endif
X		return (READ_INTR);
X	}
X
X	flush();
X	reading = 1;
X	n = read(fd, buf, len);
X	reading = 0;
X	if (n < 0)
X		return (-1);
X	return (n);
X}
X
X	public void
Xintread()
X{
X	longjmp(read_label, 1);
X}
X
X#if GET_TIME
X	public long
Xget_time()
X{
X	long t;
X
X	time(&t);
X	return (t);
X}
X#endif
X
X/*
X * Expand a string, substituting any "%" with the current filename,
X * and any "#" with the previous filename.
X */
X	public char *
Xfexpand(s)
X	char *s;
X{
X	register char *fr, *to;
X	register int n;
X	register char *e;
X
X	/*
X	 * Make one pass to see how big a buffer we 
X	 * need to allocate for the expanded string.
X	 */
X	n = 0;
X	for (fr = s;  *fr != '\0';  fr++)
X	{
X		if (*fr == '%')
X			n += strlen(current_file);
X		else if (*fr == '#')
X		{
X			if (previous_file == NULL)
X			{
X				error("No previous file");
X				return (NULL);
X			}
X			n += strlen(previous_file);
X		} else
X			n++;
X	}
X
X	e = ecalloc(n+1, sizeof(char));
X
X	/*
X	 * Now copy the string, expanding any "%" or "#".
X	 */
X	to = e;
X	for (fr = s;  *fr != '\0';  fr++)
X	{
X		if (*fr == '%')
X		{
X			strcpy(to, current_file);
X			to += strlen(to);
X		} else if (*fr == '#')
X		{
X			strcpy(to, previous_file);
X			to += strlen(to);
X		} else
X			*to++ = *fr;
X	}
X	*to = '\0';
X	return (e);
X}
X
X	static POSITION
Xseek_filesize(f)
X	int f;
X{
X	offset_t spos;
X
X	spos = lseek(f, (offset_t)0, 2);
X	if (spos == BAD_LSEEK)
X		return (NULL_POSITION);
X	return ((POSITION) spos);
X}
X
X/*
X * Expand a filename, substituting any environment variables, etc.
X * The implementation of this is necessarily very operating system
X * dependent.  This implementation is unabashedly only for Unix systems.
X */
X#if GLOB
X
XFILE *popen();
X
X	public char *
Xglob(filename)
X	char *filename;
X{
X	FILE *f;
X	char *p;
X	int ch;
X	char *cmd;
X	char *gfilename;
X
X	filename = fexpand(filename);
X	if (filename == NULL)
X		return (NULL);
X
X	/*
X	 * We get the shell to expand the filename for us by passing
X	 * an "echo" command to the shell and reading its output.
X	 */
X	p = getenv("SHELL");
X	if (p == NULL || *p == '\0')
X	{
X		/*
X		 * Read the output of <echo filename>.
X		 */
X		cmd = ecalloc(strlen(filename)+6, sizeof(char));
X		sprintf(cmd, "echo %s", filename);
X	} else
X	{
X		/*
X		 * Read the output of <$SHELL -c "echo filename">.
X		 */
X		cmd = ecalloc(strlen(p)+strlen(filename)+12, sizeof(char));
X		sprintf(cmd, "%s -c \"echo %s\"", p, filename);
X	}
X
X	f = popen(cmd, "r");
X	free(cmd);
X	if (f == NULL)
X		return (filename);
X	free(filename);
X
X	gfilename = ecalloc(FILENAME, sizeof(char));
X	for (p = gfilename;  p < &gfilename[FILENAME-1];  p++)
X	{
X		if ((ch = getc(f)) == '\n' || ch == EOF)
X			break;
X		*p = ch;
X	}
X	*p = '\0';
X	pclose(f);
X	return (gfilename);
X}
X
X#else
X
X	public char *
Xglob(filename)
X	char *filename;
X{
X	return (fexpand(filename));
X}
X
X#endif
X
X
X/*
X * Returns NULL if the file can be opened and
X * is an ordinary file, otherwise an error message
X * (if it cannot be opened or is a directory, etc.)
X */
X
X#if STAT
X
X#include <sys/types.h>
X#include <sys/stat.h>
X
X	public char *
Xbad_file(filename)
X	char *filename;
X{
X	register char *m;
X	struct stat statbuf;
X
X	if (stat(filename, &statbuf) < 0)
X		return (errno_message(filename));
X
X	if (force_open)
X		return (NULL);
X
X	if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
X	{
X		static char is_dir[] = " is a directory";
X		m = ecalloc(strlen(filename) + sizeof(is_dir), sizeof(char));
X		strcpy(m, filename);
X		strcat(m, is_dir);
X		return (m);
X	}
X	if ((statbuf.st_mode & S_IFMT) != S_IFREG)
X	{
X		static char not_reg[] = " is not a regular file";
X		m = ecalloc(strlen(filename) + sizeof(not_reg), sizeof(char));
X		strcpy(m, filename);
X		strcat(m, not_reg);
X		return (m);
X	}
X	return (NULL);
X}
X
X	public POSITION
Xfilesize(f)
X	int f;
X{
X	struct stat statbuf;
X
X	if (fstat(f, &statbuf) < 0)
X		return (seek_filesize(f));
X	return ((POSITION) statbuf.st_size);
X}
X
X#else
X
X	public char *
Xbad_file(filename)
X	char *filename;
X{
X	return (NULL);
X}
X
X	public POSITION
Xfilesize(f)
X	int f;
X{
X	return (seek_filesize(f));
X}
X
X#endif
X
X/*
X * errno_message: Return an error message based on the value of "errno".
X */
X
X#if PERROR
X
Xextern char *sys_errlist[];
Xextern int sys_nerr;
Xextern int errno;
X
X	public char *
Xerrno_message(filename)
X	char *filename;
X{
X	register char *p;
X	register char *m;
X	char msg[16];
X
X	if (errno < sys_nerr)
X		p = sys_errlist[errno];
X	else
X	{
X		sprintf(msg, "Error %d", errno);
X		p = msg;
X	}
X	m = ecalloc(strlen(filename) + strlen(p) + 3, sizeof(char));
X	sprintf(m, "%s: %s", filename, p);
X	return (m);
X}
X
X#else
X
X	public char *
Xerrno_message(filename)
X	char *filename;
X{
X	register char *m;
X	static char msg[] = ": cannot open";
X
X	m = ecalloc(strlen(filename) + sizeof(msg), sizeof(char));
X	strcpy(m, filename);
X	strcat(m, msg);
X	return (m);
X}
X
X#endif
END_OF_FILE
echo shar: Extracting \"output.c\"
sed "s/^X//" >'output.c' <<'END_OF_FILE'
X/*
X * High level routines dealing with the output to the screen.
X */
X
X#include "less.h"
X
Xpublic int errmsgs;	/* Count of messages displayed by error() */
Xstatic int need_clr;
X
Xextern int sigs;
Xextern int sc_width;
Xextern int so_width, se_width;
Xextern int screen_trashed;
Xextern int any_display;
Xextern char *first_cmd;
X
X
X/*
X * Display the line which is in the line buffer.
X */
X	public void
Xput_line()
X{
X	register int c;
X	register int i;
X
X	if (sigs)
X	{
X		/*
X		 * Don't output if a signal is pending.
X		 */
X		screen_trashed = 1;
X		return;
X	}
X
X	for (i = 0;  (c = gline(i)) != '\0';  i++)
X	{
X		switch (c)
X		{
X		case UL_CHAR:
X			ul_enter();
X			break;
X		case UE_CHAR:
X			ul_exit();
X			break;
X		case BO_CHAR:
X			bo_enter();
X			break;
X		case BE_CHAR:
X			bo_exit();
X			break;
X		case '\b':
X			putbs();
X			break;
X		default:
X			if (c & CARATBIT)
X			{
X				/*
X				 * Control characters arrive here as the
X				 * normal character [carat_char(c)] with
X				 * the CARATBIT bit set.  See pappend().
X				 */
X				putchr('^');
X				putchr(c &~ CARATBIT);
X			} else
X			{
X				putchr(c);
X			}
X		}
X	}
X}
X
X/*
X * Is a given character a "control" character?
X * {{ ASCII DEPENDENT }}
X */
X	public int
Xcontrol_char(c)
X	int c;
X{
X	return (c < ' ' || c == '\177');
X}
X
X/*
X * Return the printable character used to identify a control character
X * (printed after a carat; e.g. '\3' => "^C").
X * {{ ASCII DEPENDENT }}
X */
X	public int
Xcarat_char(c)
X	int c;
X{
X	return ((c == '\177') ? '?' : (c | 0100));
X}
X
X
Xstatic char obuf[1024];
Xstatic char *ob = obuf;
X
X/*
X * Flush buffered output.
X */
X	public void
Xflush()
X{
X	register int n;
X
X	n = ob - obuf;
X	if (n == 0)
X		return;
X	if (write(1, obuf, n) != n)
X		screen_trashed = 1;
X	ob = obuf;
X}
X
X/*
X * Discard buffered output.
X */
X	public void
Xdropout()
X{
X	ob = obuf;
X}
X
X/*
X * Output a character.
X */
X	public void
Xputchr(c)
X	int c;
X{
X	if (ob >= &obuf[sizeof(obuf)])
X		flush();
X	if (need_clr)
X	{
X		need_clr = 0;
X		lower_left();
X		clear_eol();
X	}
X	*ob++ = c;
X}
X
X/*
X * Output a string.
X */
X	public void
Xputstr(s)
X	register char *s;
X{
X	while (*s != '\0')
X		putchr(*s++);
X}
X
X/*
X * Output a message in the lower left corner of the screen
X * and wait for carriage return.
X */
X
Xstatic char return_to_continue[] = "  (press RETURN)";
X
X	public void
Xerror(s)
X	char *s;
X{
X	register int c;
X	static char buf[2] = { '\0', '\0' };
X
X	errmsgs++;
X	if (!any_display)
X	{
X		/*
X		 * Nothing has been displayed yet.
X		 * Output this message on error output (file
X		 * descriptor 2) and don't wait for a keystroke
X		 * to continue.
X		 *
X		 * This has the desirable effect of producing all
X		 * error messages on error output if standard output
X		 * is directed to a file.  It also does the same if
X		 * we never produce any real output; for example, if
X		 * the input file(s) cannot be opened.  If we do
X		 * eventually produce output, code in edit() makes
X		 * sure these messages can be seen before they are
X		 * overwritten or scrolled away.
X		 */
X		write(2, s, strlen(s));
X		write(2, "\n", 1);
X		return;
X	}
X
X	lower_left();
X	clear_eol();
X	so_enter();
X	putstr(s);
X	putstr(return_to_continue);
X	so_exit();
X
X#if ONLY_RETURN
X	while ((c = getchr()) != '\n' && c != '\r')
X		bell();
X#else
X	c = getchr();
X	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
X	{
X		buf[0] = c;
X		first_cmd = buf;
X	}
X#endif
X	lower_left();
X
X	if (strlen(s) + sizeof(return_to_continue) + 
X	    so_width + se_width + 1 > sc_width)
X		/*
X		 * Printing the message has probably scrolled the screen.
X		 * {{ Unless the terminal doesn't have auto margins,
X		 *    in which case we just hammered on the right margin. }}
X		 */
X		screen_trashed = 1;
X
X	flush();
X}
X
Xstatic char intr_to_abort[] = "... (interrupt to abort)";
X
X	public void
Xierror(s)
X	char *s;
X{
X
X	lower_left();
X	clear_eol();
X	so_enter();
X	putstr(s);
X	putstr(intr_to_abort);
X	so_exit();
X	flush();
X	need_clr = 1;
X}
END_OF_FILE
echo shar: Extracting \"position.c\"
sed "s/^X//" >'position.c' <<'END_OF_FILE'
X/*
X * Routines dealing with the "position" table.
X * This is a table which tells the position (in the input file) of the
X * first char on each currently displayed line.
X *
X * {{ The position table is scrolled by moving all the entries.
X *    Would be better to have a circular table 
X *    and just change a couple of pointers. }}
X */
X
X#include "less.h"
X#include "position.h"
X
Xstatic POSITION *table = NULL;	/* The position table */
Xstatic int table_size;
X
Xextern int sc_width, sc_height;
X
X/*
X * Return the starting file position of a line displayed on the screen.
X * The line may be specified as a line number relative to the top
X * of the screen, but is usually one of these special cases:
X *	the top (first) line on the screen
X *	the second line on the screen
X *	the bottom line on the screen
X *	the line after the bottom line on the screen
X */
X	public POSITION
Xposition(where)
X	int where;
X{
X	switch (where)
X	{
X	case BOTTOM:
X		where = sc_height - 2;
X		break;
X	case BOTTOM_PLUS_ONE:
X		where = sc_height - 1;
X		break;
X	case MIDDLE:
X		where = sc_height / 2;
X	}
X	return (table[where]);
X}
X
X/*
X * Add a new file position to the bottom of the position table.
X */
X	public void
Xadd_forw_pos(pos)
X	POSITION pos;
X{
X	register int i;
X
X	/*
X	 * Scroll the position table up.
X	 */
X	for (i = 1;  i < sc_height;  i++)
X		table[i-1] = table[i];
X	table[sc_height - 1] = pos;
X}
X
X/*
X * Add a new file position to the top of the position table.
X */
X	public void
Xadd_back_pos(pos)
X	POSITION pos;
X{
X	register int i;
X
X	/*
X	 * Scroll the position table down.
X	 */
X	for (i = sc_height - 1;  i > 0;  i--)
X		table[i] = table[i-1];
X	table[0] = pos;
X}
X
X/*
X * Initialize the position table, done whenever we clear the screen.
X */
X	public void
Xpos_clear()
X{
X	register int i;
X
X	for (i = 0;  i < sc_height;  i++)
X		table[i] = NULL_POSITION;
X}
X
X/*
X * Allocate the position table.
X */
X	public void
Xpos_init()
X{
X	if (sc_height <= table_size)
X		return;
X	if (table != NULL)
X		free((char*)table);
X	table = (POSITION *) ecalloc(sc_height, sizeof(POSITION));
X	table_size = sc_height;
X}
X
X/*
X * See if the byte at a specified position is currently on the screen.
X * Check the position table to see if the position falls within its range.
X * Return the position table entry if found, -1 if not.
X */
X	public int
Xonscreen(pos)
X	POSITION pos;
X{
X	register int i;
X
X	if (pos < table[0])
X		return (-1);
X	for (i = 1;  i < sc_height;  i++)
X		if (pos < table[i])
X			return (i-1);
X	return (-1);
X}
END_OF_FILE
echo shar: Extracting \"handle.c\"
sed "s/^X//" >'handle.c' <<'END_OF_FILE'
X/*
X * Stuff to manipulate file handles.
X * A file handle is a small integer that refers to a file name.
X * The point of all this is to avoid keeping big file names around
X * in many different places; instead we keep file HANDLES around
X * and all the names are stored away here in this module.
X * There are routines here to convert a handle to a name and vice versa.
X *
X * Also done in this module is keeping track of a file position
X * for every filename.  This is used to restore the last position
X * when we re-examine a previously examined file.
X */
X
X#include "less.h"
X
Xstruct fhandle {
X	struct fhandle *h_next;
X	HANDLE h_handle;
X	char *h_filename;
X	POSITION h_pos;
X};
X
Xstatic HANDLE xhandle = (HANDLE)0;
Xstatic struct fhandle *anchor = NULL;
X
X/*
X * Allocate a new handle structure and stick a filename in it.
X */
X	static struct fhandle *
Xnew_handle(filename)
X	char *filename;
X{
X	register struct fhandle *p;
X
X	p = (struct fhandle *) ecalloc(1, sizeof(struct fhandle));
X	p->h_handle = ++xhandle;
X	p->h_filename = filename;
X	p->h_next = anchor;
X	p->h_pos = (POSITION)0;
X	anchor = p;
X	return (p);
X}
X
X/*
X * Find a handle structure, given a handle.
X */
X	static struct fhandle *
Xh_to_fh(handle)
X	HANDLE handle;
X{
X	register struct fhandle *p;
X
X	for (p = anchor;  p != NULL;  p = p->h_next)
X		if (handle == p->h_handle)
X			return (p);
X
X	error("ERROR: h_to_fh");
X	quit();
X	/*NOTREACHED*/
X}
X
X/*
X * Find a handle structure, given a filename.
X */
X	static struct fhandle *
Xfn_to_fh(filename)
X	char *filename;
X{
X	register struct fhandle *p;
X
X	for (p = anchor;  p != NULL;  p = p->h_next)
X		if (strcmp(filename, p->h_filename) == 0)
X			return (p);
X	return (NULL);
X}
X
X/*
X * Get the handle associated with a filename.
X */
X	public HANDLE
Xget_handle(filename)
X	char *filename;
X{
X	register struct fhandle *p;
X
X	if ((p = fn_to_fh(filename)) == NULL)
X		p = new_handle(save(filename));
X	return (p->h_handle);
X}
X
X/*
X * Stash away a filename.
X * The filename must be known to be STATIC;
X * this function, unlike get_handle(), 
X * does not make a copy of the filename.
X */
X	public void
Xsave_handle(filename)
X	char *filename;
X{
X	if (fn_to_fh(filename) == NULL)
X		(void) new_handle(filename);
X}
X
X/*
X * Get the filename associated with a handle.
X */
X	public char *
Xget_filename(handle)
X	HANDLE handle;
X{
X	register struct fhandle *p;
X
X	p = h_to_fh(handle);
X	return (p->h_filename);
X}
X
X/*
X * Save the current position in a given file.
X */
X	public void
Xstore_pos(handle, pos)
X	HANDLE handle;
X	POSITION pos;
X{
X	register struct fhandle *p;
X
X	p = h_to_fh(handle);
X	p->h_pos = pos;
X}
X
X/*
X * Recall the current position in a file which may have been seen before.
X * If it hasn't been seen before, return NULL_POSITION.
X */
X	public POSITION
Xrecall_pos(handle)
X	HANDLE handle;
X{
X	register struct fhandle *p;
X
X	p = h_to_fh(handle);
X	return (p->h_pos);
X}
END_OF_FILE

mark@unix386.Convergent.COM (Mark Nudelman) (09/15/89)

#! /bin/sh
# This is a shell archive.
# Remove anything before this line, then unpack it
# by saving it into a file and typing "sh file".

echo shar: Extracting \"prim1.c\"
sed "s/^X//" >'prim1.c' <<'END_OF_FILE'
X/*
X * Primitives for displaying the file on the screen.
X */
X
X#include "less.h"
X#include "position.h"
X
Xpublic int hit_eof;	/* Keeps track of how many times we hit end of file */
Xpublic int screen_trashed;
X
Xstatic int squished;
X
Xextern int sigs;
Xextern int top_scroll;
Xextern int quiet;
Xextern int sc_width, sc_height;
Xextern int quit_at_eof;
Xextern int plusoption;
Xextern char *first_cmd;
X#if TAGS
Xextern int tagoption;
X#endif
X
X/*
X * Sound the bell to indicate user is trying to move past end of file.
X */
X	static void
Xeof_bell()
X{
X	if (quiet == NOT_QUIET)
X		bell();
X	else
X		vbell();
X}
X
X/*
X * Check to see if the end of file is currently "displayed".
X */
X	static void
Xeof_check()
X{
X	POSITION pos;
X
X	if (sigs)
X		return;
X	/*
X	 * If the bottom line is empty, we are at EOF.
X	 * If the bottom line ends at the file length,
X	 * we must be just at EOF.
X	 */
X	pos = position(BOTTOM_PLUS_ONE);
X	if (pos == NULL_POSITION || pos == ch_length())
X		hit_eof++;
X}
X
X/*
X * If the screen is "squished", repaint it.
X * "Squished" means the first displayed line is not at the top
X * of the screen; this can happen when we display a short file
X * for the first time.
X */
X	static void
Xsquish_check()
X{
X	if (!squished)
X		return;
X	squished = 0;
X	repaint();
X}
X
X/*
X * Display n lines, scrolling forward, 
X * starting at position pos in the input file.
X * "force" means display the n lines even if we hit end of file.
X * "only_last" means display only the last screenful if n > screen size.
X */
X	static void
Xforw(n, pos, force, only_last)
X	register int n;
X	POSITION pos;
X	int force;
X	int only_last;
X{
X	int eof = 0;
X	int nlines = 0;
X	int do_repaint;
X	static int first_time = 1;
X
X	squish_check();
X
X	/*
X	 * do_repaint tells us not to display anything till the end, 
X	 * then just repaint the entire screen.
X	 */
X	do_repaint = (only_last && n > sc_height-1);
X
X	if (!do_repaint)
X	{
X		if (top_scroll && n >= sc_height - 1)
X		{
X			/*
X			 * Start a new screen.
X			 * {{ This is not really desirable if we happen
X			 *    to hit eof in the middle of this screen,
X			 *    but we don't yet know if that will happen. }}
X			 */
X			if (top_scroll == 2)
X				clear();
X			home();
X			force = 1;
X		} else
X		{
X			lower_left();
X			clear_eol();
X		}
X
X		if (pos != position(BOTTOM_PLUS_ONE))
X		{
X			/*
X			 * This is not contiguous with what is
X			 * currently displayed.  Clear the screen image 
X			 * (position table) and start a new screen.
X			 */
X			pos_clear();
X			add_forw_pos(pos);
X			force = 1;
X			if (top_scroll)
X			{
X				if (top_scroll == 2)
X					clear();
X				home();
X			} else if (!first_time)
X			{
X				putstr("...skipping...\n");
X			}
X		}
X	}
X
X	while (--n >= 0)
X	{
X		/*
X		 * Read the next line of input.
X		 */
X		pos = forw_line(pos);
X		if (pos == NULL_POSITION)
X		{
X			/*
X			 * End of file: stop here unless the top line 
X			 * is still empty, or "force" is true.
X			 */
X			eof = 1;
X			if (!force && position(TOP) != NULL_POSITION)
X				break;
X		}
X		/*
X		 * Add the position of the next line to the position table.
X		 * Display the current line on the screen.
X		 */
X		add_forw_pos(pos);
X		nlines++;
X		if (do_repaint)
X			continue;
X		/*
X		 * If this is the first screen displayed and
X		 * we hit an early EOF (i.e. before the requested
X		 * number of lines), we "squish" the display down
X		 * at the bottom of the screen.
X		 * But don't do this if a + option or a -t option
X		 * was given.  These options can cause us to
X		 * start the display after the beginning of the file,
X		 * and it is not appropriate to squish in that case.
X		 */
X		if (first_time && pos == NULL_POSITION && !top_scroll && 
X#if TAGS
X		    !tagoption &&
X#endif
X		    !plusoption)
X		{
X			squished = 1;
X			continue;
X		}
X		if (top_scroll == 1)
X			clear_eol();
X		put_line();
X	}
X
X	if (eof && !sigs)
X		hit_eof++;
X	else
X		eof_check();
X	if (nlines == 0)
X		eof_bell();
X	else if (do_repaint)
X		repaint();
X	first_time = 0;
X	(void) currline(BOTTOM);
X}
X
X/*
X * Display n lines, scrolling backward.
X */
X	static void
Xback(n, pos, force, only_last)
X	register int n;
X	POSITION pos;
X	int force;
X	int only_last;
X{
X	int nlines = 0;
X	int do_repaint;
X
X	squish_check();
X	do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
X	hit_eof = 0;
X	while (--n >= 0)
X	{
X		/*
X		 * Get the previous line of input.
X		 */
X		pos = back_line(pos);
X		if (pos == NULL_POSITION)
X		{
X			/*
X			 * Beginning of file: stop here unless "force" is true.
X			 */
X			if (!force)
X				break;
X		}
X		/*
X		 * Add the position of the previous line to the position table.
X		 * Display the line on the screen.
X		 */
X		add_back_pos(pos);
X		nlines++;
X		if (!do_repaint)
X		{
X			home();
X			add_line();
X			put_line();
X		}
X	}
X
X	eof_check();
X	if (nlines == 0)
X		eof_bell();
X	else if (do_repaint)
X		repaint();
X	(void) currline(BOTTOM);
X}
X
X/*
X * Display n more lines, forward.
X * Start just after the line currently displayed at the bottom of the screen.
X */
X	public void
Xforward(n, only_last)
X	int n;
X	int only_last;
X{
X	POSITION pos;
X
X	if (quit_at_eof && hit_eof)
X	{
X		/*
X		 * If the -e flag is set and we're trying to go
X		 * forward from end-of-file, go on to the next file.
X		 */
X		next_file(1);
X		return;
X	}
X
X	pos = position(BOTTOM_PLUS_ONE);
X	if (pos == NULL_POSITION)
X	{
X		eof_bell();
X		hit_eof++;
X		return;
X	}
X	forw(n, pos, 0, only_last);
X}
X
X/*
X * Display n more lines, backward.
X * Start just before the line currently displayed at the top of the screen.
X */
X	public void
Xbackward(n, only_last)
X	int n;
X	int only_last;
X{
X	POSITION pos;
X
X	pos = position(TOP);
X	if (pos == NULL_POSITION)
X	{
X		/* 
X		 * This will almost never happen,
X		 * because the top line is almost never empty. 
X		 */
X		eof_bell();
X		return;   
X	}
X	back(n, pos, 0, only_last);
X}
X
X/*
X * Repaint the screen, starting from a specified position.
X */
X	static void
Xprepaint(pos)	
X	POSITION pos;
X{
X	hit_eof = 0;
X	squished = 0;
X	screen_trashed = 0;
X	forw(sc_height-1, pos, 1, 0);
X}
X
X/*
X * Repaint the screen.
X */
X	public void
Xrepaint()
X{
X	/*
X	 * Start at the line currently at the top of the screen
X	 * and redisplay the screen.
X	 */
X	prepaint(position(TOP));
X}
X
X/*
X * Jump to the end of the file.
X * It is more convenient to paint the screen backward,
X * from the end of the file toward the beginning.
X */
X	public void
Xjump_forw()
X{
X	POSITION pos;
X
X	if (ch_end_seek())
X	{
X		error("Cannot seek to end of file");
X		return;
X	}
X	lastmark();
X	pos = ch_tell();
X	clear();
X	pos_clear();
X	add_back_pos(pos);
X	screen_trashed = 0;
X	back(sc_height - 1, pos, 0, 0);
X}
X
X/*
X * Jump to line n in the file.
X */
X	public void
Xjump_back(n)
X	int n;
X{
X	POSITION pos;
X	char m[50];
X
X	pos = find_pos(n);
X	if (pos != NULL_POSITION && ch_seek(pos) == 0)
X	{
X		jump_loc(pos);
X	} else if (n <= 1 && ch_beg_seek() == 0)
X	{
X		jump_loc(ch_tell());
X		error("Cannot seek to beginning of file");
X	} else
X	{
X		sprintf(m, "Cannot seek to line number %d", n);
X		error(m);
X	}
X}
X
X/*
X * Jump to a specified position in the file.
X * The position must be the first character in a line.
X */
X	public void
Xjump_loc(pos)
X	POSITION pos;
X{
X	register int nline;
X	POSITION tpos;
X
X	if ((nline = onscreen(pos)) >= 0)
X	{
X		/*
X		 * The line is currently displayed.  
X		 * Just scroll there.
X		 */
X		forw(nline, position(BOTTOM_PLUS_ONE), 1, 0);
X		return;
X	}
X
X	/*
X	 * Line is not on screen.
X	 * Seek to the desired location.
X	 */
X	if (ch_seek(pos))
X	{
X		error("Cannot seek to that file position");
X		return;
X	}
X
X	/*
X	 * See if the desired line is BEFORE the currently
X	 * displayed screen.  If so, then move forward far
X	 * enough so the line we're on will be at the bottom
X	 * of the screen, in order to be able to call back()
X	 * to make the screen scroll backwards & put the line
X	 * at the top of the screen.
X	 * {{ This seems inefficient, but it's not so bad,
X	 *    since we can never move forward more than a
X	 *    screenful before we stop to redraw the screen. }}
X	 */
X	tpos = position(TOP);
X	if (tpos != NULL_POSITION && pos < tpos)
X	{
X		POSITION npos = pos;
X		/*
X		 * Note that we can't forw_line() past tpos here,
X		 * so there should be no EOI at this stage.
X		 */
X		for (nline = 0;  npos < tpos && nline < sc_height - 1;  nline++)
X			npos = forw_line(npos);
X
X		if (npos < tpos)
X		{
X			/*
X			 * More than a screenful back.
X			 */
X			lastmark();
X			clear();
X			pos_clear();
X			add_back_pos(npos);
X		}
X
X		/*
X		 * Note that back() will repaint() if nline > back_scroll.
X		 */
X		back(nline, npos, 1, 0);
X		return;
X	}
X	/*
X	 * Remember where we were; clear and paint the screen.
X	 */
X  	lastmark();
X  	prepaint(pos);
X}
END_OF_FILE
echo shar: Extracting \"prim2.c\"
sed "s/^X//" >'prim2.c' <<'END_OF_FILE'
X/*
X * More primitives for displaying the file on the screen.
X */
X
X#include "less.h"
X#include "position.h"
X
Xextern int sigs;
Xextern int how_search;
Xextern int top_scroll;
Xextern int back_scroll;
Xextern int caseless;
Xextern int linenums;
Xextern int sc_height;
Xextern HANDLE curr_handle;
X
X/*
X * Jump to a specified position in the file.
X * The position need not be the first character in a line.
X */
X	static void
Xjump_line_loc(pos)
X	POSITION pos;
X{
X	int c;
X
X	if (ch_seek(pos) == 0)
X	{
X		/*
X		 * Back up to the beginning of the line.
X		 */
X		while ((c = ch_back_get()) != '\n' && c != EOI)
X			;
X		if (c == '\n')
X			(void) ch_forw_get();
X		pos = ch_tell();
X	}
X	jump_loc(pos);
X}
X
X/*
X * Jump to a specified percentage into the file.
X * This is a poor compensation for not being able to
X * quickly jump to a specific line number.
X */
X	public void
Xjump_percent(percent)
X	int percent;
X{
X	POSITION pos, len;
X
X	/*
X	 * Determine the position in the file
X	 * (the specified percentage of the file's length).
X	 */
X	if ((len = ch_length()) == NULL_POSITION)
X	{
X		error("Don't know length of file");
X		return;
X	}
X	pos = (percent * len) / 100;
X
X	jump_line_loc(pos);
X}
X
X/*
X * The table of marks.
X * A mark is simply a position in a file and the handle of the file.
X */
X#define	NMARKS		(27)		/* 26 for a-z plus one for quote */
X#define	LASTMARK	(NMARKS-1)	/* For quote */
X
Xstatic struct mark {
X	HANDLE m_handle;
X	POSITION m_pos;
X} marks[NMARKS];
X
X/*
X * Initialize the mark table to show no marks are set.
X */
X	public void
Xinit_mark()
X{
X	int i;
X
X	for (i = 0;  i < NMARKS;  i++)
X		marks[i].m_pos = NULL_POSITION;
X}
X
X/*
X * See if a mark letter is valid (between a and z).
X */
X	static int
Xbadmark(c)
X	int c;
X{
X	if (c < 'a' || c > 'z')
X	{
X		error("Choose a letter between 'a' and 'z'");
X		return (1);
X	}
X	return (0);
X}
X
X/*
X * Set a mark.
X */
X	public void
Xsetmark(c)
X	int c;
X{
X	if (badmark(c))
X		return;
X	c -= 'a';
X	marks[c].m_pos = position(TOP);
X	marks[c].m_handle = curr_handle;
X}
X
X/*
X * Set the LASTMARK (the mark named by the apostrophe).
X */
X	public void
Xlastmark()
X{
X	POSITION pos;
X
X	pos = position(TOP);
X	if (pos == NULL_POSITION)
X		return;
X	marks[LASTMARK].m_pos = pos;
X	marks[LASTMARK].m_handle = curr_handle;
X}
X
X/*
X * Go to a previously set mark.
X */
X	public void
Xgomark(c)
X	int c;
X{
X	POSITION pos;
X	HANDLE handle;
X
X	if (c == '\'')
X		c = LASTMARK;
X	else if (badmark(c))
X		return;
X	else 
X		c -= 'a';
X
X	pos = marks[c].m_pos;
X	handle = marks[c].m_handle;
X	if (pos == NULL_POSITION)
X	{
X		error("Mark not set");
X		return;
X	}
X
X	if (handle != curr_handle)
X		/*
X		 * Not in the current file; edit the correct file.
X		 */
X		edit(get_filename(handle));
X
X	jump_loc(pos);
X}
X
X/*
X * Get the backwards scroll limit.
X * Must call this function instead of just using the value of
X * back_scroll, because the default case depends on sc_height and
X * top_scroll, as well as back_scroll.
X */
X	public int
Xget_back_scroll()
X{
X	if (back_scroll >= 0)
X		return (back_scroll);
X	if (top_scroll)
X		return (sc_height - 2);
X	return (10000); /* infinity */
X}
X
X/*
X * Try to match the n-th bracket of the specified type 
X * which appears in the top displayed line.
X * brac may be '\0' to mean look for any bracket.
X * "Bracket" refers to any of the pairs: { }, [ ], or ( ).
X */
X	public void
Xmatch_brac(brac, n)
X	register int brac;
X	int n;
X{
X	register int c;
X	register int nest;
X	int obrac, cbrac;
X	int forwdir;
X	POSITION pos;
X	int (*chget)();
X
X	extern int ch_forw_get(), ch_back_get();
X
X	pos = position(TOP);
X	if (pos == NULL_POSITION || ch_seek(pos))
X	{
X		error("Nothing in top line");
X		return;
X	}
X
X	/*
X	 * Look thru the first line to find the type of bracket to match.
X	 */
X	for (;;)
X	{
X		if ((c = ch_forw_get()) == '\n' || c == EOI)
X		{
X			error("No bracket in top line");
X			return;
X		}
X		if (brac != '\0' && brac != c)
X			/*
X			 * This is not the specified bracket character.
X			 */
X			continue;
X
X		switch (c)
X		{
X		default:  continue;
X		case '{':   obrac = '{';  cbrac = '}';  forwdir = 1;  break;
X		case '}':   obrac = '}';  cbrac = '{';  forwdir = 0;  break;
X		case '[':   obrac = '[';  cbrac = ']';  forwdir = 1;  break;
X		case ']':   obrac = ']';  cbrac = '[';  forwdir = 0;  break;
X		case '(':   obrac = '(';  cbrac = ')';  forwdir = 1;  break;
X		case ')':   obrac = ')';  cbrac = '(';  forwdir = 0;  break;
X		}
X		/*
X		 * See if we have the n-th bracket in the line.
X		 */
X		if (--n <= 0)
X			break;
X	}
X
X	if (!forwdir)
X	{
X		/*
X		 * Position the file just "after" the open bracket.
X		 * That is, if searching backwards, skip back over 
X		 * the open bracket now.
X		 */
X		(void) ch_back_get();
X	}
X
X	/*
X	 * Search the file for the matching bracket.
X	 */
X	chget = (forwdir) ? ch_forw_get : ch_back_get;
X	nest = 0;
X	while ((c = (*chget)()) != EOI)
X	{
X		if (c == obrac)
X			nest++;
X		else if (c == cbrac && --nest < 0)
X		{
X			jump_line_loc(ch_tell());
X			return;
X		}
X	}
X	error("No matching bracket");
X}
X
X/*
X * Search for the n-th occurrence of a specified pattern, 
X * either forward or backward.
X */
X	public void
Xsearch(search_type, pattern, n)
X	int search_type;
X	char *pattern;
X	register int n;
X{
X	POSITION pos, linepos;
X	register char *p;
X	register char *q;
X	register int goforw;
X	register int wantmatch;
X	char *line;
X	int linenum;
X	int linematch;
X#if RECOMP
X	char *re_comp();
X	char *errmsg;
X#else
X#if REGCMP
X	char *regcmp();
X	static char *cpattern = NULL;
X#else
X	static char lpbuf[100];
X	static char *last_pattern = NULL;
X#endif
X#endif
X
X	/*
X	 * Extract flags and type of search.
X	 */
X	wantmatch = !(search_type & SRCH_NOMATCH);
X	search_type = SRCH_TYPE(search_type);
X
X	if (caseless && pattern != NULL)
X	{
X		/*
X		 * For a caseless search, convert any uppercase
X		 * in the pattern to lowercase.
X		 */
X		for (p = pattern;  *p != '\0';  p++)
X			if (*p >= 'A' && *p <= 'Z')
X				*p += 'a' - 'A';
X	}
X#if RECOMP
X
X	/*
X	 * (re_comp handles a null pattern internally, 
X	 *  so there is no need to check for a null pattern here.)
X	 */
X	if ((errmsg = re_comp(pattern)) != NULL)
X	{
X		error(errmsg);
X		return;
X	}
X#else
X#if REGCMP
X	if (pattern == NULL || *pattern == '\0')
X	{
X		/*
X		 * A null pattern means use the previous pattern.
X		 * The compiled previous pattern is in cpattern, so just use it.
X		 */
X		if (cpattern == NULL)
X		{
X			error("No previous regular expression");
X			return;
X		}
X	} else
X	{
X		/*
X		 * Otherwise compile the given pattern.
X		 */
X		char *s;
X		if ((s = regcmp(pattern, 0)) == NULL)
X		{
X			error("Invalid pattern");
X			return;
X		}
X		if (cpattern != NULL)
X			free(cpattern);
X		cpattern = s;
X	}
X#else
X	if (pattern == NULL || *pattern == '\0')
X	{
X		/*
X		 * Null pattern means use the previous pattern.
X		 */
X		if (last_pattern == NULL)
X		{
X			error("No previous regular expression");
X			return;
X		}
X		pattern = last_pattern;
X	} else
X	{
X		strcpy(lpbuf, pattern);
X		last_pattern = lpbuf;
X	}
X#endif
X#endif
X
X	/*
X	 * Figure out where to start the search.
X	 */
X
X	if (search_type == SRCH_FILE)
X	{
X		/*
X		 * User wants to start searching at beginning of file.
X		 * {{ Use ch_beg_seek() in case we can't seek to 0? }}
X		 */
X		pos = (POSITION)0;
X		goforw = 1;
X	} else if (position(TOP) == NULL_POSITION)
X	{
X		/*
X		 * Nothing is currently displayed.
X		 * Start at the beginning of the file.
X		 * (This case is mainly for first_cmd searches,
X		 * for example, "+/xyz" on the command line.)
X		 */
X		pos = (POSITION)0;
X		goforw = 1;
X	} else if (search_type == SRCH_BACK)
X	{
X		/*
X		 * Backward search: start just before the top line
X		 * displayed on the screen.
X		 */
X		pos = position(TOP);
X		goforw = 0;
X	} else if (how_search == 0)
X	{
X		/*
X		 * Start at the second real line displayed on the screen.
X		 */
X		pos = position(TOP);
X		do
X			pos = forw_raw_line(pos, (char **)NULL);
X		while (pos < position(TOP_PLUS_ONE));
X		goforw = 1;
X	} else if (how_search == 1)
X	{
X		/*
X		 * Start just after the bottom line displayed on the screen.
X		 */
X		pos = position(BOTTOM_PLUS_ONE);
X		goforw = 1;
X	} else
X	{
X		/*
X		 * Start at the second screen line displayed on the screen.
X		 */
X		pos = position(TOP_PLUS_ONE);
X		goforw = 1;
X	}
X
X	if (pos == NULL_POSITION)
X	{
X		/*
X		 * Can't find anyplace to start searching from.
X		 */
X		error("Nothing to search");
X		return;
X	}
X
X	linenum = find_linenum(pos);
X	for (;;)
X	{
X		/*
X		 * Get lines until we find a matching one or 
X		 * until we hit end-of-file (or beginning-of-file 
X		 * if we're going backwards).
X		 */
X		if (sigs)
X			/*
X			 * A signal aborts the search.
X			 */
X			return;
X
X		if (goforw)
X		{
X			/*
X			 * Read the next line, and save the 
X			 * starting position of that line in linepos.
X			 */
X			linepos = pos;
X			pos = forw_raw_line(pos, &line);
X			if (linenum != 0)
X				linenum++;
X		} else
X		{
X			/*
X			 * Read the previous line and save the
X			 * starting position of that line in linepos.
X			 */
X			pos = back_raw_line(pos, &line);
X			linepos = pos;
X			if (linenum != 0)
X				linenum--;
X		}
X
X		if (pos == NULL_POSITION)
X		{
X			/*
X			 * We hit EOF/BOF without a match.
X			 */
X			error("Pattern not found");
X			return;
X		}
X
X		/*
X		 * If we're using line numbers, we might as well
X		 * remember the information we have now (the position
X		 * and line number of the current line).
X		 */
X		if (linenums)
X			add_lnum(linenum, pos);
X
X		if (caseless)
X		{
X			/*
X			 * If this is a caseless search, convert 
X			 * uppercase in the input line to lowercase.
X			 * While we're at it, remove any backspaces
X			 * along with the preceding char.
X			 * This allows us to match text which is 
X			 * underlined or overstruck.
X			 */
X			for (p = q = line;  *p != '\0';  p++, q++)
X			{
X				if (*p >= 'A' && *p <= 'Z')
X					/* Convert uppercase to lowercase. */
X					*q = *p + 'a' - 'A';
X				else if (q > line && *p == '\b')
X					/* Delete BS and preceding char. */
X					q -= 2;
X				else
X					/* Otherwise, just copy. */
X					*q = *p;
X			}
X		}
X
X		/*
X		 * Test the next line to see if we have a match.
X		 * This is done in a variety of ways, depending
X		 * on what pattern matching functions are available.
X		 */
X#if REGCMP
X		linematch = (regex(cpattern, line) != NULL);
X#else
X#if RECOMP
X		linematch = (re_exec(line) == 1);
X#else
X		linematch = match(pattern, line);
X#endif
X#endif
X		/*
X		 * We are successful if wantmatch and linematch are
X		 * both true (want a match and got it),
X		 * or both false (want a non-match and got it).
X		 */
X		if (((wantmatch && linematch) || (!wantmatch && !linematch)) &&
X		      --n <= 0)
X			/*
X			 * Found the line.
X			 */
X			break;
X	}
X
X	jump_loc(linepos);
X}
X
X#if (!REGCMP) && (!RECOMP)
X/*
X * We have neither regcmp() nor re_comp().
X * We use this function to do simple pattern matching.
X * It supports no metacharacters like *, etc.
X */
X	static int
Xmatch(pattern, buf)
X	char *pattern, *buf;
X{
X	register char *pp, *lp;
X
X	for ( ;  *buf != '\0';  buf++)
X	{
X		for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
X			if (*pp == '\0' || *lp == '\0')
X				break;
X		if (*pp == '\0')
X			return (1);
X	}
X	return (0);
X}
X#endif
END_OF_FILE
echo shar: Extracting \"prompt.c\"
sed "s/^X//" >'prompt.c' <<'END_OF_FILE'
X/*
X * Prompting and other messages.
X * There are three flavors of prompts, SHORT, MEDIUM and LONG,
X * selected by the -m/-M options.
X * There is also the "equals message", printed by the = command.
X * A prompt is a message composed of various pieces, such as the 
X * name of the file being viewed, the percentage into the file, etc.
X */
X
X#include "less.h"
X#include "position.h"
X
Xextern int pr_type;
Xextern int ispipe;
Xextern int hit_eof;
Xextern int new_file;
Xextern int sc_width;
Xextern int so_width, se_width;
Xextern char *current_file;
Xextern int ac;
Xextern char **av;
Xextern int curr_ac;
Xextern int linenums;
X#if EDITOR
Xextern char *editor;
X#endif
X
X/*
X * Prototypes for the three flavors of prompts.
X * These strings are expanded by pr_expand().
X */
Xstatic char s_proto[] =
X  "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t";
Xstatic char m_proto[] =
X  "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
Xstatic char M_proto[] =
X  "?f%f .?n?m(file %i of %m) ..?ltline %lt?L/%L. :byte %bB?s/%s. .?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t";
Xstatic char e_proto[] =
X  "?f%f .?m(file %i of %m) .?ltline %lt?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
X
Xpublic char *prproto[3];
Xpublic char *eqproto = e_proto;
X
Xstatic char message[250];
Xstatic char *mp;
X
X/*
X * Initialize the prompt prototype strings.
X */
X	public void
Xinit_prompt()
X{
X	prproto[0] = save(s_proto);
X	prproto[1] = save(m_proto);
X	prproto[2] = save(M_proto);
X	eqproto = save(e_proto);
X}
X
X/*
X * Set the message pointer to the end of the message string.
X */
X	static void
Xsetmp()
X{
X	while (*mp != '\0')
X		mp++;
X}
X
X/*
X * Append a POSITION (as a decimal integer) to the end of the message.
X */
X	static void
Xap_pos(pos)
X	POSITION pos;
X{
X	sprintf(mp, "%ld", (long)pos);
X	setmp();
X}
X
X/*
X * Append an integer to the end of the message.
X */
X	static void
Xap_int(n)
X	int n;
X{
X	sprintf(mp, "%d", n);
X	setmp();
X}
X
X/*
X * Append a string to the end of the message.
X */
X	static void
Xap_str(s)
X	char *s;
X{
X	strtcpy(mp, s, (unsigned int)(&message[sizeof(message)] - mp));
X	setmp();
X}
X
X/*
X * Append a question mark to the end of the message.
X */
X	static void
Xap_quest()
X{
X	*mp++ = '?';
X}
X
X/*
X * Return the "current" byte offset in the file.
X */
X	static POSITION
Xcurr_byte(where)
X	int where;
X{
X	POSITION pos;
X
X	pos = position(where);
X	if (pos == NULL_POSITION)
X		pos = ch_length();
X	return (pos);
X}
X
X/*
X * Return the value of a prototype conditional.
X * A prototype string may include conditionals which consist of a 
X * question mark followed by a single letter.
X * Here we decode that letter and return the appropriate boolean value.
X */
X	static int
Xcond(c, where)
X	char c;
X	int where;
X{
X	switch (c)
X	{
X	case 'a':	/* Anything in the message yet? */
X		return (mp > message);
X	case 'b':	/* Current byte offset known? */
X		return (curr_byte(where) != NULL_POSITION);
X	case 'e':	/* At end of file? */
X		return (hit_eof);
X	case 'f':	/* Filename known? */
X		return (!ispipe);
X	case 'l':	/* Line number known? */
X		return (linenums);
X	case 'L':	/* Final line number known? */
X		return (linenums && ch_length() != NULL_POSITION);
X	case 'm':	/* More than one file? */
X		return (ac > 1);
X	case 'n':	/* First prompt in a new file? */
X		return (new_file);
X	case 'p':	/* Percent into file known? */
X		return (curr_byte(where) != NULL_POSITION && 
X				ch_length() > 0);
X	case 's':	/* Size of file known? */
X		return (ch_length() != NULL_POSITION);
X	case 'x':	/* Is there a "next" file? */
X		return (curr_ac + 1 < ac);
X	}
X	return (0);
X}
X
X/*
X * Decode a "percent" prototype character.
X * A prototype string may include various "percent" escapes;
X * that is, a percent sign followed by a single letter.
X * Here we decode that letter and take the appropriate action,
X * usually by appending something to the message being built.
X */
X	static void
Xprotochar(c, where)
X	int c;
X	int where;
X{
X	POSITION pos;
X	POSITION len;
X	int n;
X
X	switch (c)
X	{
X	case 'b':	/* Current byte offset */
X		pos = curr_byte(where);
X		if (pos != NULL_POSITION)
X			ap_pos(pos);
X		else
X			ap_quest();
X		break;
X#if EDITOR
X	case 'E':	/* Editor name */
X		ap_str(editor);
X		break;
X#endif
X	case 'f':	/* File name */
X		ap_str(current_file);
X		break;
X	case 'i':	/* Index into list of files */
X		ap_int(curr_ac + 1);
X		break;
X	case 'l':	/* Current line number */
X		n = currline(where);
X		if (n != 0)
X			ap_int(n);
X		else
X			ap_quest();
X		break;
X	case 'L':	/* Final line number */
X		len = ch_length();
X		if (len == NULL_POSITION || len == (POSITION)0 ||
X		    (n = find_linenum(len)) <= 0)
X			ap_quest();
X		else
X			ap_int(n-1);
X		break;
X	case 'm':	/* Number of files */
X		ap_int(ac);
X		break;
X	case 'p':	/* Percent into file */
X		pos = curr_byte(where);
X		len = ch_length();
X		if (pos != NULL_POSITION && len > 0)
X			ap_int((int)(100*pos / len));
X		else
X			ap_quest();
X		break;
X	case 's':	/* Size of file */
X		len = ch_length();
X		if (len != NULL_POSITION)
X			ap_pos(len);
X		else
X			ap_quest();
X		break;
X	case 't':	/* Truncate trailing spaces in the message */
X		while (mp > message && mp[-1] == ' ')
X			mp--;
X		break;
X	case 'x':	/* Name of next file */
X		if (curr_ac + 1 < ac)
X			ap_str(av[curr_ac+1]);
X		else
X			ap_quest();
X		break;
X	}
X}
X
X/*
X * Skip a false conditional.
X * When a false condition is found (either a false IF or the ELSE part 
X * of a true IF), this routine scans the prototype string to decide
X * where to resume parsing the string.
X * We must keep track of nested IFs and skip them properly.
X */
X	static char *
Xskipcond(p)
X	register char *p;
X{
X	register int iflevel;
X
X	/*
X	 * We came in here after processing a ? or :,
X	 * so we start nested one level deep.
X	 */
X	iflevel = 1;
X
X	for (;;) switch (*++p)
X	{
X	case '?':
X		/*
X		 * Start of a nested IF.
X		 */
X		iflevel++;
X		break;
X	case ':':
X		/*
X		 * Else.
X		 * If this matches the IF we came in here with,
X		 * then we're done.
X		 */
X		if (iflevel == 1)
X			return (p);
X		break;
X	case '.':
X		/*
X		 * Endif.
X		 * If this matches the IF we came in here with,
X		 * then we're done.
X		 */
X		if (--iflevel == 0)
X			return (p);
X		break;
X	case '\\':
X		/*
X		 * Backslash escapes the next character.
X		 */
X		++p;
X		break;
X	case '\0':
X		/*
X		 * Whoops.  Hit end of string.
X		 * This is a malformed conditional, but just treat it
X		 * as if all active conditionals ends here.
X		 */
X		return (p-1);
X	}
X	/*NOTREACHED*/
X}
X
X	static char *
Xwherechar(p, wp)
X	char *p;
X	int *wp;
X{
X	switch (*p)
X	{
X	case 'b': case 'l': case 'p':
X		switch (*++p)
X		{
X		case 't':   *wp = TOP;			break;
X		case 'm':   *wp = MIDDLE;		break;
X		case 'b':   *wp = BOTTOM;		break;
X		case 'B':   *wp = BOTTOM_PLUS_ONE;	break;
X		default:    *wp = TOP;			break;
X		}
X	}
X	return (p);
X}
X
X/*
X * Construct a message based on a prototype string.
X */
X	public char *
Xpr_expand(proto, maxwidth)
X	char *proto;
X	int maxwidth;
X{
X	register char *p;
X	register int c;
X	int where;
X
X	mp = message;
X
X	if (*proto == '\0')
X		return ("");
X
X	for (p = proto;  *p != '\0';  p++)
X	{
X		switch (*p)
X		{
X		default:	/* Just put the character in the message */
X			*mp++ = *p;
X			break;
X		case '\\':	/* Backslash escapes the next character */
X			p++;
X			*mp++ = *p;
X			break;
X		case '?':	/* Conditional (IF) */
X			if ((c = *++p) == '\0')
X				--p;
X			else
X			{
X				p = wherechar(p, &where);
X				if (!cond(c, where))
X					p = skipcond(p);
X			}
X			break;
X		case ':':	/* ELSE */
X			p = skipcond(p);
X			break;
X		case '.':	/* ENDIF */
X			break;
X		case '%':	/* Percent escape */
X			if ((c = *++p) == '\0')
X				--p;
X			else
X			{
X				p = wherechar(p, &where);
X				protochar(c, where);
X			}
X			break;
X		}
X	}
X
X	new_file = 0;
X	if (mp == message)
X		return (NULL);
X	*mp = '\0';
X	if (maxwidth > 0 && mp >= message + maxwidth)
X	{
X		/*
X		 * Message is too long.
X		 * Return just the final portion of it.
X		 */
X		return (mp - maxwidth);
X	}
X	return (message);
X}
X
X/*
X * Return a message suitable for printing by the "=" command.
X */
X	public char *
Xeq_message()
X{
X	return (pr_expand(eqproto, 0));
X}
X
X/*
X * Return a prompt.
X * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
X * If we can't come up with an appropriate prompt, return NULL
X * and the caller will prompt with a colon.
X */
X	public char *
Xpr_string()
X{
X	return (pr_expand(prproto[pr_type], sc_width-so_width-se_width-2));
X}
END_OF_FILE
echo shar: Extracting \"screen.c\"
sed "s/^X//" >'screen.c' <<'END_OF_FILE'
X/*
X * Routines which deal with the characteristics of the terminal.
X * Uses termcap to be as terminal-independent as possible.
X *
X * {{ Someday this should be rewritten to use curses. }}
X */
X
X#include "less.h"
X#if XENIX
X#include <sys/types.h>
X#include <sys/ioctl.h>
X#endif
X
X#if TERMIO
X#include <termio.h>
X#else
X#include <sgtty.h>
X#endif
X
X#ifdef TIOCGWINSZ
X#include <sys/ioctl.h>
X#else
X/*
X * For the Unix PC (ATT 7300 & 3B1):
X * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
X * whether to include sys/window.h.  Use SIGPHONE from sys/signal.h instead.
X */
X#include <sys/signal.h>
X#ifdef SIGPHONE
X#include <sys/window.h>
X#endif
X#endif
X
X/*
X * Strings passed to tputs() to do various terminal functions.
X */
Xstatic char
X	*sc_pad,		/* Pad string */
X	*sc_home,		/* Cursor home */
X	*sc_addline,		/* Add line, scroll down following lines */
X	*sc_lower_left,		/* Cursor to last line, first column */
X	*sc_move,		/* General cursor positioning */
X	*sc_clear,		/* Clear screen */
X	*sc_eol_clear,		/* Clear to end of line */
X	*sc_s_in,		/* Enter standout (highlighted) mode */
X	*sc_s_out,		/* Exit standout mode */
X	*sc_u_in,		/* Enter underline mode */
X	*sc_u_out,		/* Exit underline mode */
X	*sc_b_in,		/* Enter bold mode */
X	*sc_b_out,		/* Exit bold mode */
X	*sc_visual_bell,	/* Visual bell (flash screen) sequence */
X	*sc_backspace,		/* Backspace cursor */
X	*sc_init,		/* Startup terminal initialization */
X	*sc_deinit;		/* Exit terminal de-initialization */
X
Xpublic int auto_wrap;		/* Terminal does \r\n when write past margin */
Xpublic int ignaw;		/* Terminal ignores \n immediately after wrap */
Xpublic int erase_char, kill_char; /* The user's erase and line-kill chars */
Xpublic int sc_width, sc_height;	/* Height & width of screen */
Xpublic int bo_width, be_width;	/* Printing width of boldface sequences */
Xpublic int ul_width, ue_width;	/* Printing width of underline sequences */
Xpublic int so_width, se_width;	/* Printing width of standout sequences */
X
Xstatic char *cheaper();
X
X/*
X * These two variables are sometimes defined in,
X * and needed by, the termcap library.
X * It may be necessary on some systems to declare them extern here.
X */
X/*extern*/ short ospeed;	/* Terminal output baud rate */
X/*extern*/ char PC;		/* Pad character */
X
Xextern int quiet;		/* If VERY_QUIET, use visual bell for bell */
Xextern int know_dumb;		/* Don't complain about a dumb terminal */
Xextern int back_scroll;
Xextern int swindow;
Xextern char *tgetstr();
Xextern char *tgoto();
X
X/*
X * Change terminal to "raw mode", or restore to "normal" mode.
X * "Raw mode" means 
X *	1. An outstanding read will complete on receipt of a single keystroke.
X *	2. Input is not echoed.  
X *	3. On output, \n is mapped to \r\n.
X *	4. \t is NOT expanded into spaces.
X *	5. Signal-causing characters such as ctrl-C (interrupt),
X *	   etc. are NOT disabled.
X * It doesn't matter whether an input \n is mapped to \r, or vice versa.
X */
X	public void
Xraw_mode(on)
X	int on;
X{
X#if TERMIO
X	struct termio s;
X	static struct termio save_term;
X
X	if (on)
X	{
X		/*
X		 * Get terminal modes.
X		 */
X		ioctl(2, TCGETA, &s);
X
X		/*
X		 * Save modes and set certain variables dependent on modes.
X		 */
X		save_term = s;
X		ospeed = s.c_cflag & CBAUD;
X		erase_char = s.c_cc[VERASE];
X		kill_char = s.c_cc[VKILL];
X
X		/*
X		 * Set the modes to the way we want them.
X		 */
X		s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
X		s.c_oflag |=  (OPOST|ONLCR|TAB3);
X		s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
X		s.c_cc[VMIN] = 1;
X		s.c_cc[VTIME] = 0;
X	} else
X	{
X		/*
X		 * Restore saved modes.
X		 */
X		s = save_term;
X	}
X	ioctl(2, TCSETAW, &s);
X#else
X	struct sgttyb s;
X	static struct sgttyb save_term;
X
X	if (on)
X	{
X		/*
X		 * Get terminal modes.
X		 */
X		ioctl(2, TIOCGETP, &s);
X
X		/*
X		 * Save modes and set certain variables dependent on modes.
X		 */
X		save_term = s;
X		ospeed = s.sg_ospeed;
X		erase_char = s.sg_erase;
X		kill_char = s.sg_kill;
X
X		/*
X		 * Set the modes to the way we want them.
X		 */
X		s.sg_flags |= CBREAK;
X		s.sg_flags &= ~(ECHO|XTABS);
X	} else
X	{
X		/*
X		 * Restore saved modes.
X		 */
X		s = save_term;
X	}
X	ioctl(2, TIOCSETN, &s);
X#endif
X}
X
X	static void
Xcannot(s)
X	char *s;
X{
X	char message[100];
X
X	if (know_dumb)
X		/* 
X		 * User knows this is a dumb terminal, so don't tell him.
X		 */
X		return;
X
X	sprintf(message, "WARNING: terminal cannot %s", s);
X	error(message);
X}
X
X/*
X * Get terminal capabilities via termcap.
X */
X	public void
Xget_term()
X{
X	char *sp;
X	register char *t1, *t2;
X	register int hard;
X	char *term;
X#ifdef TIOCGWINSZ
X	struct winsize w;
X#else
X#ifdef WIOCGETD
X	struct uwdata w;
X#endif
X#endif
X	char termbuf[2048];
X
X	static char sbuf[1024];
X
X	extern char *getenv();
X
X	/*
X	 * Find out what kind of terminal this is.
X	 */
X 	if ((term = getenv("TERM")) == NULL)
X 		term = "unknown";
X 	if (tgetent(termbuf, term) <= 0)
X 		strcpy(termbuf, "dumb:co#80:hc:");
X
X	/*
X	 * Get size of the screen.
X	 */
X#ifdef TIOCGWINSZ
X	if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
X		sc_height = w.ws_row;
X	else
X#else
X#ifdef WIOCGETD
X	if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
X		sc_height = w.uw_height/w.uw_vs;
X	else
X#endif
X#endif
X 		sc_height = tgetnum("li");
X
X 	hard = (sc_height <= 0 || tgetflag("hc"));
X	if (hard)
X	{
X		/* Oh no, this is a hardcopy terminal. */
X		sc_height = 24;
X	}
X
X	pos_init();
X	if (swindow < 0)
X		swindow = sc_height - 1;
X
X#ifdef TIOCGWINSZ
X 	if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
X		sc_width = w.ws_col;
X	else
X#ifdef WIOCGETD
X	if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
X		sc_width = w.uw_width/w.uw_hs;
X	else
X#endif
X#endif
X 		sc_width = tgetnum("co");
X
X 	if (sc_width <= 0)
X  		sc_width = 80;
X
X	auto_wrap = tgetflag("am");
X	ignaw = tgetflag("xn");
X
X	/*
X	 * Assumes termcap variable "sg" is the printing width of:
X	 * the standout sequence, the end standout sequence,
X	 * the underline sequence, the end underline sequence,
X	 * the boldface sequence, and the end boldface sequence.
X	 */
X	if ((so_width = tgetnum("sg")) < 0)
X		so_width = 0;
X	be_width = bo_width = ue_width = ul_width = se_width = so_width;
X
X	/*
X	 * Get various string-valued capabilities.
X	 */
X	sp = sbuf;
X
X	sc_pad = tgetstr("pc", &sp);
X	if (sc_pad != NULL)
X		PC = *sc_pad;
X
X	sc_init = tgetstr("ti", &sp);
X	if (sc_init == NULL)
X		sc_init = "";
X
X	sc_deinit= tgetstr("te", &sp);
X	if (sc_deinit == NULL)
X		sc_deinit = "";
X
X	sc_eol_clear = tgetstr("ce", &sp);
X	if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
X	{
X		cannot("clear to end of line");
X		sc_eol_clear = "";
X	}
X
X	sc_clear = tgetstr("cl", &sp);
X	if (hard || sc_clear == NULL || *sc_clear == '\0')
X	{
X		cannot("clear screen");
X		sc_clear = "\n\n";
X	}
X
X	sc_move = tgetstr("cm", &sp);
X	if (hard || sc_move == NULL || *sc_move == '\0')
X	{
X		/*
X		 * This is not an error here, because we don't 
X		 * always need sc_move.
X		 * We need it only if we don't have home or lower-left.
X		 */
X		sc_move = "";
X	}
X
X	sc_s_in = tgetstr("so", &sp);
X	if (hard || sc_s_in == NULL)
X		sc_s_in = "";
X
X	sc_s_out = tgetstr("se", &sp);
X	if (hard || sc_s_out == NULL)
X		sc_s_out = "";
X
X	sc_u_in = tgetstr("us", &sp);
X	if (hard || sc_u_in == NULL)
X		sc_u_in = sc_s_in;
X
X	sc_u_out = tgetstr("ue", &sp);
X	if (hard || sc_u_out == NULL)
X		sc_u_out = sc_s_out;
X
X	sc_b_in = tgetstr("md", &sp);
X	if (hard || sc_b_in == NULL)
X	{
X		sc_b_in = sc_s_in;
X		sc_b_out = sc_s_out;
X	} else
X	{
X		sc_b_out = tgetstr("me", &sp);
X		if (hard || sc_b_out == NULL)
X			sc_b_out = "";
X	}
X
X	sc_visual_bell = tgetstr("vb", &sp);
X	if (hard || sc_visual_bell == NULL)
X		sc_visual_bell = "";
X
X	if (tgetflag("bs"))
X		sc_backspace = "\b";
X	else
X	{
X		sc_backspace = tgetstr("bc", &sp);
X		if (sc_backspace == NULL || *sc_backspace == '\0')
X			sc_backspace = "\b";
X	}
X
X	/*
X	 * Choose between using "ho" and "cm" ("home" and "cursor move")
X	 * to move the cursor to the upper left corner of the screen.
X	 */
X	t1 = tgetstr("ho", &sp);
X	if (hard || t1 == NULL)
X		t1 = "";
X	if (*sc_move == '\0')
X		t2 = "";
X	else
X	{
X		strcpy(sp, tgoto(sc_move, 0, 0));
X		t2 = sp;
X		sp += strlen(sp) + 1;
X	}
X	sc_home = cheaper(t1, t2, "home cursor", "|\b^");
X
X	/*
X	 * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
X	 * to move the cursor to the lower left corner of the screen.
X	 */
X	t1 = tgetstr("ll", &sp);
X	if (hard || t1 == NULL)
X		t1 = "";
X	if (*sc_move == '\0')
X		t2 = "";
X	else
X	{
X		strcpy(sp, tgoto(sc_move, 0, sc_height-1));
X		t2 = sp;
X		sp += strlen(sp) + 1;
X	}
X	sc_lower_left = cheaper(t1, t2,
X		"move cursor to lower left of screen", "\r");
X
X	/*
X	 * Choose between using "al" or "sr" ("add line" or "scroll reverse")
X	 * to add a line at the top of the screen.
X	 */
X	t1 = tgetstr("al", &sp);
X	if (hard || t1 == NULL)
X		t1 = "";
X	t2 = tgetstr("sr", &sp);
X	if (hard || t2 == NULL)
X		t2 = "";
X	sc_addline = cheaper(t1, t2, "scroll backwards", "");
X	if (*sc_addline == '\0')
X	{
X		/*
X		 * Force repaint on any backward movement.
X		 */
X		back_scroll = 0;
X	}
X}
X
X/*
X * Return the "best" of the two given termcap strings.
X * The best, if both exist, is the one with the lower 
X * cost (see cost() function).
X */
X	static char *
Xcheaper(t1, t2, doit, def)
X	char *t1, *t2;
X	char *doit;
X	char *def;
X{
X	if (*t1 == '\0' && *t2 == '\0')
X	{
X		cannot(doit);
X		return (def);
X	}
X	if (*t1 == '\0')
X		return (t2);
X	if (*t2 == '\0')
X		return (t1);
X	if (cost(t1) < cost(t2))
X		return (t1);
X	return (t2);
X}
X
X/*
X * Return the cost of displaying a termcap string.
X * We use the trick of calling tputs, but as a char printing function
X * we give it inc_costcount, which just increments "costcount".
X * This tells us how many chars would be printed by using this string.
X * {{ Couldn't we just use strlen? }}
X */
Xstatic int costcount;
X
X/*ARGSUSED*/
X	static void
Xinc_costcount(c)
X	int c;
X{
X	costcount++;
X}
X
X	static int
Xcost(t)
X	char *t;
X{
X	costcount = 0;
X	tputs(t, sc_height, inc_costcount);
X	return (costcount);
X}
X
X
X/*
X * Below are the functions which perform all the 
X * terminal-specific screen manipulation.
X */
X
X
X/*
X * Initialize terminal
X */
X	public void
Xinit()
X{
X	tputs(sc_init, sc_height, putchr);
X}
X
X/*
X * Deinitialize terminal
X */
X	public void
Xdeinit()
X{
X	tputs(sc_deinit, sc_height, putchr);
X}
X
X/*
X * Home cursor (move to upper left corner of screen).
X */
X	public void
Xhome()
X{
X	tputs(sc_home, 1, putchr);
X}
X
X/*
X * Add a blank line (called with cursor at home).
X * Should scroll the display down.
X */
X	public void
Xadd_line()
X{
X	tputs(sc_addline, sc_height, putchr);
X}
X
X/*
X * Move cursor to lower left corner of screen.
X */
X	public void
Xlower_left()
X{
X	tputs(sc_lower_left, 1, putchr);
X}
X
X/*
X * Ring the terminal bell.
X */
X	public void
Xbell()
X{
X	if (quiet == VERY_QUIET)
X		vbell();
X	else
X		putchr('\7');
X}
X
X/*
X * Output the "visual bell", if there is one.
X */
X	public void
Xvbell()
X{
X	if (*sc_visual_bell == '\0')
X		return;
X	tputs(sc_visual_bell, sc_height, putchr);
X}
X
X/*
X * Clear the screen.
X */
X	public void
Xclear()
X{
X	tputs(sc_clear, sc_height, putchr);
X}
X
X/*
X * Clear from the cursor to the end of the cursor's line.
X * {{ This must not move the cursor. }}
X */
X	public void
Xclear_eol()
X{
X	tputs(sc_eol_clear, 1, putchr);
X}
X
X/*
X * Begin "standout" (bold, underline, or whatever).
X */
X	public void
Xso_enter()
X{
X	tputs(sc_s_in, 1, putchr);
X}
X
X/*
X * End "standout".
X */
X	public void
Xso_exit()
X{
X	tputs(sc_s_out, 1, putchr);
X}
X
X/*
X * Begin "underline" (hopefully real underlining, 
X * otherwise whatever the terminal provides).
X */
X	public void
Xul_enter()
X{
X	tputs(sc_u_in, 1, putchr);
X}
X
X/*
X * End "underline".
X */
X	public void
Xul_exit()
X{
X	tputs(sc_u_out, 1, putchr);
X}
X
X/*
X * Begin "bold"
X */
X	public void
Xbo_enter()
X{
X	tputs(sc_b_in, 1, putchr);
X}
X
X/*
X * End "bold".
X */
X	public void
Xbo_exit()
X{
X	tputs(sc_b_out, 1, putchr);
X}
X
X/*
X * Erase the character to the left of the cursor 
X * and move the cursor left.
X */
X	public void
Xbackspace()
X{
X	/* 
X	 * Try to erase the previous character by overstriking with a space.
X	 */
X	tputs(sc_backspace, 1, putchr);
X	putchr(' ');
X	tputs(sc_backspace, 1, putchr);
X}
X
X/*
X * Output a plain backspace, without erasing the previous char.
X */
X	public void
Xputbs()
X{
X	tputs(sc_backspace, 1, putchr);
X}
END_OF_FILE
echo shar: Extracting \"signal.c\"
sed "s/^X//" >'signal.c' <<'END_OF_FILE'
X/*
X * Routines dealing with signals.
X *
X * A signal usually merely causes a bit to be set in the "signals" word.
X * At some convenient time, the mainline code checks to see if any
X * signals need processing by calling psignal().
X * If we happen to be reading from a file [in iread()] at the time
X * the signal is received, we call intread to interrupt the iread.
X */
X
X#include "less.h"
X#include <signal.h>
X
X/*
X * "sigs" contains bits indicating signals which need to be processed.
X */
Xpublic int sigs;
X
X#define	S_INTERRUPT	01
X#ifdef SIGTSTP
X#define	S_STOP		02
X#endif
X#if defined(SIGWINCH) || defined(SIGWIND)
X#define S_WINCH		04
X#endif
X
Xextern int sc_width, sc_height;
Xextern int swindow;
Xextern int screen_trashed;
Xextern int lnloop;
Xextern int linenums;
Xextern int scroll;
Xextern int reading;
X
X/*
X * Interrupt signal handler.
X */
X	static HANDLER
Xinterrupt()
X{
X	SIGNAL(SIGINT, interrupt);
X	sigs |= S_INTERRUPT;
X	if (reading)
X		intread();
X}
X
X#ifdef SIGTSTP
X/*
X * "Stop" (^Z) signal handler.
X */
X	static HANDLER
Xstop()
X{
X	SIGNAL(SIGTSTP, stop);
X	sigs |= S_STOP;
X	if (reading)
X		intread();
X}
X#endif
X
X#ifdef SIGWINCH
X/*
X * "Window" change handler
X */
X	public HANDLER
Xwinch()
X{
X	SIGNAL(SIGWINCH, winch);
X	sigs |= S_WINCH;
X	if (reading)
X		intread();
X}
X#else
X#ifdef SIGWIND
X/*
X * "Window" change handler
X */
X	public HANDLER
Xwinch()
X{
X	SIGNAL(SIGWIND, winch);
X	sigs |= S_WINCH;
X	if (reading)
X		intread();
X}
X#endif
X#endif
X
X/*
X * Set up the signal handlers.
X */
X	public void
Xinit_signals(on)
X	int on;
X{
X	if (on)
X	{
X		/*
X		 * Set signal handlers.
X		 */
X		(void) SIGNAL(SIGINT, interrupt);
X#ifdef SIGTSTP
X		(void) SIGNAL(SIGTSTP, stop);
X#endif
X#ifdef SIGWINCH
X		(void) SIGNAL(SIGWINCH, winch);
X#else
X#ifdef SIGWIND
X		(void) SIGNAL(SIGWIND, winch);
X#endif
X#endif
X	} else
X	{
X		/*
X		 * Restore signals to defaults.
X		 */
X		(void) SIGNAL(SIGINT, SIG_DFL);
X#ifdef SIGTSTP
X		(void) SIGNAL(SIGTSTP, SIG_DFL);
X#endif
X#ifdef SIGWINCH
X		(void) SIGNAL(SIGWINCH, SIG_IGN);
X#endif
X#ifdef SIGWIND
X		(void) SIGNAL(SIGWIND, SIG_IGN);
X#endif
X	}
X}
X
X/*
X * Process any signals we have received.
X * A received signal cause a bit to be set in "sigs".
X */
X	public void
Xpsignals()
X{
X	register int tsignals;
X
X	if ((tsignals = sigs) == 0)
X		return;
X	sigs = 0;
X
X#ifdef S_WINCH
X	if (tsignals & S_WINCH)
X	{
X		int old_width, old_height;
X		/*
X		 * Re-execute get_term() to read the new window size.
X		 */
X		old_width = sc_width;
X		old_height = sc_height;
X		swindow = -1;
X		get_term();
X		if (sc_width != old_width || sc_height != old_height)
X		{
X			scroll = (sc_height + 1) / 2;
X			screen_trashed = 1;
X		}
X	}
X#endif
X#ifdef SIGTSTP
X	if (tsignals & S_STOP)
X	{
X		/*
X		 * Clean up the terminal.
X		 */
X#ifdef SIGTTOU
X		SIGNAL(SIGTTOU, SIG_IGN);
X#endif
X		lower_left();
X		clear_eol();
X		deinit();
X		flush();
X		raw_mode(0);
X#ifdef SIGTTOU
X		SIGNAL(SIGTTOU, SIG_DFL);
X#endif
X		SIGNAL(SIGTSTP, SIG_DFL);
X		kill(getpid(), SIGTSTP);
X		/*
X		 * ... Bye bye. ...
X		 * Hopefully we'll be back later and resume here...
X		 * Reset the terminal and arrange to repaint the
X		 * screen when we get back to the main command loop.
X		 */
X		SIGNAL(SIGTSTP, stop);
X		raw_mode(1);
X		init();
X		screen_trashed = 1;
X	}
X#endif
X	if (tsignals & S_INTERRUPT)
X	{
X		bell();
X		/*
X		 * {{ You may wish to replace the bell() with 
X		 *    error("Interrupt"); }}
X		 */
X
X		/*
X		 * If we were interrupted while in the "calculating 
X		 * line numbers" loop, turn off line numbers.
X		 */
X		if (lnloop)
X		{
X			lnloop = 0;
X			if (linenums == 2)
X				screen_trashed = 1;
X			linenums = 0;
X			error("Line numbers turned off");
X		}
X
X	}
X}
END_OF_FILE
echo shar: Extracting \"tags.c\"
sed "s/^X//" >'tags.c' <<'END_OF_FILE'
X#include <stdio.h>
X#include "less.h"
X
X#define	WHITESP(c)	((c)==' ' || (c)=='\t')
X
X#if TAGS
X
Xpublic char *tagfile;
Xpublic char *tagpattern;
X
Xstatic char *tags = "tags";
X
Xextern int linenums;
Xextern int sigs;
X
X/*
X * Find a tag in the "tags" file.
X * Sets "tagfile" to the name of the file containing the tag,
X * and "tagpattern" to the search pattern which should be used
X * to find the tag.
X */
X	public int
Xfindtag(tag)
X	register char *tag;
X{
X	register char *p;
X	register FILE *f;
X	register int taglen;
X	int search_char;
X	static char tline[200];
X
X	if ((f = fopen(tags, "r")) == NULL)
X	{
X		error("No tags file");
X		tagfile = NULL;
X		return;
X	}
X
X	taglen = strlen(tag);
X
X	/*
X	 * Search the tags file for the desired tag.
X	 */
X	while (fgets(tline, sizeof(tline), f) != NULL)
X	{
X		if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
X			continue;
X
X		/*
X		 * Found it.
X		 * The line contains the tag, the filename and the
X		 * pattern, separated by white space.
X		 * The pattern is surrounded by a pair of identical
X		 * search characters.
X		 * Parse the line and extract these parts.
X		 */
X		tagfile = tagpattern = NULL;
X
X		/*
X		 * Skip over the whitespace after the tag name.
X		 */
X		for (p = tline;  !WHITESP(*p) && *p != '\0';  p++)
X			continue;
X		while (WHITESP(*p))
X			p++;
X		if (*p == '\0')
X			/* File name is missing! */
X			continue;
X
X		/*
X		 * Save the file name.
X		 * Skip over the whitespace after the file name.
X		 */
X		tagfile = p;
X		while (!WHITESP(*p) && *p != '\0')
X			p++;
X		*p++ = '\0';
X		while (WHITESP(*p))
X			p++;
X		if (*p == '\0')
X			/* Pattern is missing! */
X			continue;
X
X		/*
X		 * Save the pattern.
X		 * Skip to the end of the pattern.
X		 * Delete the initial "^" and the final "$" from the pattern.
X		 */
X		search_char = *p++;
X		if (*p == '^')
X			p++;
X		tagpattern = p;
X		while (*p != search_char && *p != '\0')
X			p++;
X		if (p[-1] == '$')
X			p--;
X		*p = '\0';
X
X		fclose(f);
X		return;
X	}
X	fclose(f);
X	error("No such tag in tags file");
X	tagfile = NULL;
X}
X
X/*
X * Search for a tag.
X * This is a stripped-down version of search().
X * We don't use search() for several reasons:
X *   -	We don't want to blow away any search string we may have saved.
X *   -	The various regular-expression functions (from different systems:
X *	regcmp vs. re_comp) behave differently in the presence of 
X *	parentheses (which are almost always found in a tag).
X */
X	public int
Xtagsearch()
X{
X	POSITION pos, linepos;
X	int linenum;
X	char *line;
X
X	pos = (POSITION)0;
X	linenum = find_linenum(pos);
X
X	for (;;)
X	{
X		/*
X		 * Get lines until we find a matching one or 
X		 * until we hit end-of-file.
X		 */
X		if (sigs)
X			return (1);
X
X		/*
X		 * Read the next line, and save the 
X		 * starting position of that line in linepos.
X		 */
X		linepos = pos;
X		pos = forw_raw_line(pos, &line);
X		if (linenum != 0)
X			linenum++;
X
X		if (pos == NULL_POSITION)
X		{
X			/*
X			 * We hit EOF without a match.
X			 */
X			error("Tag not found");
X			return (1);
X		}
X
X		/*
X		 * If we're using line numbers, we might as well
X		 * remember the information we have now (the position
X		 * and line number of the current line).
X		 */
X		if (linenums)
X			add_lnum(linenum, pos);
X
X		/*
X		 * Test the line to see if we have a match.
X		 * Use strncmp because the pattern may be
X		 * truncated (in the tags file) if it is too long.
X		 */
X		if (strncmp(tagpattern, line, strlen(tagpattern)) == 0)
X			break;
X	}
X
X	jump_loc(linepos);
X	return (0);
X}
X
X#endif
END_OF_FILE
echo shar: Extracting \"ttyin.c\"
sed "s/^X//" >'ttyin.c' <<'END_OF_FILE'
X/*
X * Routines dealing with getting input from the keyboard (i.e. from the user).
X */
X
X#include "less.h"
X
Xstatic int tty;
X
X/*
X * Open keyboard for input.
X * (Just use file descriptor 2.)
X */
X	public void
Xopen_getchr()
X{
X	tty = 2;
X}
X
X/*
X * Get a character from the keyboard.
X */
X	public int
Xgetchr()
X{
X	char c;
X	int result;
X
X	do
X	{
X		result = iread(tty, &c, sizeof(char));
X		if (result == READ_INTR)
X			return (READ_INTR);
X		if (result < 0)
X		{
X			/*
X			 * Don't call error() here,
X			 * because error calls getchr!
X			 */
X			quit();
X		}
X	} while (result != 1 || c == '\0');
X	return (c);
X}
END_OF_FILE
echo shar: Extracting \"version.c\"
sed "s/^X//" >'version.c' <<'END_OF_FILE'
X/*
X *		less
X *	Copyright (c) 1984,1985,1989  Mark Nudelman
X *
X *	This program may be freely used and/or modified, 
X *	with the following provisions:
X *	1. This notice and the above copyright notice must remain intact.
X *	2. Neither this program, nor any modification of it,
X *	   may be sold for profit without written consent of the author.
X *
X *	-----------------------------------------------------------------
X *	Special note to whoever ported "less" to the Amiga:
X *	If you're going to be vain enough to splash your name on
X *	the screen every time someone runs less, you might at
X *	least credit the author.
X *	-----------------------------------------------------------------
X *
X *	This program is a paginator similar to "more", 
X *	but allows you to move both forward and backward in the file.  
X *	Commands are based on "more" and "vi".
X *
X *	----------------------- CHANGES ---------------------------------
X *
X *	    Allowed use on standard input		1/29/84   markn
X *	    Added E, N, P commands			2/1/84    markn
X *	    Added '=' command, 'stop' signal handling	4/17/84   markn
X *	    Added line folding				4/20/84   markn
X *	v2: Fixed '=' command to use BOTTOM_PLUS_ONE, 
X *	    instead of TOP, added 'p' & 'v' commands	4/27/84   markn
X *	v3: Added -m and -t options, '-' command	5/3/84    markn
X *	v4: Added LESS environment variable		5/3/84    markn
X *	v5: New comments, fixed '-' command slightly	5/3/84    markn
X *	v6: Added -Q, visual bell			5/15/84   markn
X *	v7: Fixed jump_back(n) bug: n should count real
X *	    lines, not folded lines.  Also allow number
X *	    on G command.				5/24/84   markn
X *	v8: Re-do -q and -Q commands			5/30/84   markn
X *	v9: Added "+<cmd>" argument			9/25/84   markn
X *	v10: Fixed bug in -b<n> argument processing	10/10/84  markn
X *	v11: Made error() ring bell if \n not entered.	10/18/84  markn
X *	-----------------------------------------------------------------
X *	v12: Reorganized signal handling and made
X *	     portable to 4.2bsd.			2/13/85   mark
X *	v13: Reword error message for '-' command.	2/16/85   mark
X *	v14: Added -bf and -bp variants of -b.		2/22/85   mark
X *	v15: Miscellaneous changes.			2/25/85   mark
X *	v16: Added -u flag for backspace processing.	3/13/85   mark
X *	v17: Added j and k commands, 
X *		changed -t default.			4/13/85   mark
X *	v18: Rewrote signal handling code.		4/20/85   mark
X *	v19: Got rid of "verbose" eq_message().		5/2/85    mark
X *	     Made search() scroll in some cases.
X *	v20: Fixed screen.c ioctls for System V.	5/21/85   mark
X *	v21: Fixed some first_cmd bugs.			5/23/85   mark
X *	v22: Added support for no RECOMP nor REGCMP.	5/24/85   mark
X * 	v23: Miscellanous changes and prettying up.	5/25/85   mark
X *		Posted to USENET.
X *	-----------------------------------------------------------------
X *      v24: Added ti,te terminal init & de-init       6/3/85 Mike Kersenbrock
X *	v25: Added -U flag, standout mode underlining.	6/8/85    mark
X *	v26: Added -M flag.				6/9/85    mark
X *	     Use underline termcap (us) if it exists.
X *	v27: Renamed some variables to make unique in	6/15/85   mark
X *	     6 chars.  Minor fix to -m.
X *	v28: Fixed right margin bug.			6/28/85   mark
X *	v29: Incorporated M.Rose's changes to signal.c	6/28/85   mark
X *	v30: Fixed stupid bug in argument processing.	6/29/85   mark
X *	v31: Added -p flag, changed repaint algorithm.  7/15/85   mark
X *	     Added kludge for magic cookie terminals.
X *	v32: Added cat_file if output not a tty.	7/16/85   mark
X *	v33: Added -e flag and EDITOR.			7/23/85   mark
X *	v34: Added -s flag.				7/26/85   mark
X *	v35: Rewrote option handling; added option.c.	7/27/85   mark
X *	v36: Fixed -e flag to work if not last file.	7/29/85   mark
X *	v37: Added -x flag.				8/10/85   mark
X *	v38: Changed prompting; created prompt.c.	8/19/85   mark
X *	v39: (Not -p) does not initially clear screen.	8/24/85   mark
X *	v40: Added "skipping" indicator in forw().	8/26/85   mark
X *		Posted to USENET.
X *	-----------------------------------------------------------------
X *	v41: ONLY_RETURN, control char commands,	9/17/85   mark
X *	     faster search, other minor fixes.
X *	v42: Added ++ command line syntax;		9/25/85   mark
X *	     ch_fsize for pipes.
X *	v43: Added -h flag, changed prim.c algorithms.	10/15/85  mark
X *	v44: Made END print in all cases of eof;	10/16/85  mark
X *	     ignore SIGTTOU after receiving SIGTSTP.
X *	v45: Never print backspaces unless -u.		10/16/85  mark
X *	v46: Backwards scroll in jump_loc.		10/24/85  mark
X *	v47: Fixed bug in edit(): *first_cmd==0		10/30/85  mark
X *	v48: Use TIOCSETN instead of TIOCSETP.		11/16/85  mark
X *	     Added marks (m and ' commands).
X *		Posted to USENET.
X *	-----------------------------------------------------------------
X *	v49: Fixed bug: signal didn't clear mcc.	1/9/86    mark
X *	v50: Added ' (quote) to gomark.			1/15/86   mark
X *	v51: Added + cmd, fixed problem if first_cmd
X *	     fails, made g cmd sort of "work" on pipes
X *	     even if bof is no longer buffered.		1/16/86   mark
X *	v52: Made short files work better.		1/17/86   mark
X *	v53: Added -P option.				1/20/86   mark
X *	v54: Changed help to use HELPFILE.		1/20/86   mark
X *	v55: Messages work better if not tty output.	1/23/86   mark
X *	v56: Added -l option.				1/24/86   mark
X *	v57: Fixed -l to get confirmation before
X *	     overwriting an existing file.		1/31/86   mark
X *	v58: Added filename globbing.			8/28/86   mark
X *	v59: Fixed some bugs with very long filenames.	9/15/86   mark
X *	v60: Incorporated changes from Leith (Casey)
X *	     Leedom for boldface and -z option.		9/26/86   mark
X *	v61: Got rid of annoying repaints after ! cmd.	9/26/86   mark
X *		Posted to USENET.
X *	-----------------------------------------------------------------
X *	v62: Added is_directory(); change -z default to
X *	     -1 instead of 24; cat-and-exit if -e and
X *	     file is less than a screenful.		12/23/86  mark
X *	v63: Fixed bug in cat-and-exit if > 1 file.	1/8/87    mark
X *	v64: Changed puts/putstr, putc/putchr, 
X *	     getc/getchr to avoid name conflict with 
X *	     stdio functions.				1/12/87  mark
X *	v65: Allowed '-' command to change NUMBER
X *	     valued options (thanks to Gary Puckering)	1/26/87  mark
X *	v66: Fixed bug: prepaint should use force=1.	2/13/87  mark
X *	v67: Added !! and % expansion to ! command.	2/24/87  mark
X *	v68: Added SIGWINCH and TIOCGWINSZ support;
X *	     changed is_directory to bad_file.
X *	     (thanks to J. Robert Ward)			2/25/87  mark
X *	v69: Added SIGWIND and WIOCGETD (for Unix PC).	2/25/87  mark
X *	v70: Changed help cmd from 'h' to 'H'; better 
X *	     error msgs in bad_file, errno_message.	3/13/87  mark
X *	v71: Changed -p to -c, made triple -c/-C
X *	     for clear-eol like more's -c.		5/11/87  mark
X *	v72: Added -E, -L, use $SHELL in lsystem().	6/26/87  mark
X *	     (thanks to Steve Spearman)
X *	v73: Allow Examine "#" for previous file.	6/26/87  mark
X *		Posted to USENET 8/25/87.
X *	-----------------------------------------------------------------
X *	v74: Fix conflict in EOF symbol with stdio.h,	9/18/87  mark
X *	     Make os.c more portable to BSD.
X *	v75: Fix problems in get_term (thanks to 	9/23/87  mark
X *	     Paul Eggert); new backwards scrolling in
X *	     jump_loc (thanks to Marion Hakanson).
X *	v76: Added -i flag; allow single "!" to		9/23/87  mark
X *	     invoke a shell (thanks to Franco Barber).
X *	v77: Added -n flag and line number support.	9/24/87  mark
X *	v78: Fixed problem with prompts longer than	9/25/87  mark
X *	     the screen width.	
X *	v79: Added the _ command.			9/29/87  mark
X *	v80: Allow signal to break out of linenum scan.	10/6/87  mark
X *	v81: Allow -b to be changed from within less.	10/6/87  mark
X *	v82: Add cmd_decode to use a table for key	10/7/87  mark
X *	     binding (thanks to David Nason).
X *	v83: Allow .less file for user-defined keys.	10/9/87  mark
X *	v84: Fix -e/-E problems (thanks to Felix Lee).	10/11/87 mark
X *	v85: Search now keeps track of line numbers.	10/15/87 mark
X *	v86: Added -B option and autobuf; fixed		10/20/87 mark
X *	     "pipe error" bug.
X *	v87: Fix bug re BSD signals while reading file.	3/1/88   mark
X *	v88: Use new format for -P option (thanks to	3/12/88  mark
X *	     der Mouse), allow "+-c" without message,
X *	     fix bug re BSD hangup.
X *	v89: Turn off line numbers if linenum scan	3/18/88  mark
X *	     is interrupted.
X *	v90: Allow -P from within less.			3/30/88  mark
X *	v91: Added tags file support (new -t option)	3/30/88  mark
X *	     (thanks to Brian Campbell).
X *	v92: Added -+option syntax.			4/4/88   mark
X *	v93: Add support for slow input (thanks to	4/11/88  mark
X *	     Joe Orost & apologies for taking almost
X *	     3 years to get this in!)
X *	v94: Redo reading/signal stuff.			4/11/88  mark
X *	v95: Repaint screen better after signal.	4/20/88  mark
X *	v96: Add /! and ?! commands.			4/21/88  mark
X *	v97: Allow -l/-L from within less.		5/17/88  mark
X *	     Eliminate some static arrays (use calloc).
X *		Posted to USENET.
X *	-----------------------------------------------------------------
X *	v98: Fix incorrect calloc call; uninitialized	10/14/88 mark
X *	     var in exec_mca; core dump on unknown TERM.
X *	     Make v cmd work if past last line of file.
X *	     Fix some signal bugs.
X *	v99: Allow space between -X and string,		10/29/88 mark
X *	     when X is a string-valued option.
X *	v100: Fix globbing bug when $SHELL not set;	1/5/89   mark
X *	      allow spaces after -t command.
X *	v101: Fix problem with long (truncated) lines	1/6/89   mark
X *	      in tags file (thanks to Neil Dixon).
X *	v102: Fix bug with E# when no prev file;	1/6/89   mark
X *	      allow spaces after -l command.
X *	v103: Add -N, -f and -? options.  Add z and w	3/14/89  mark
X *	      commands.  Add %L for prompt strings.
X *	v104: Added EDITPROTO.				3/16/89  mark
X *	v105: Fix bug in find_linenum which cached	3/20/89  mark
X *	      incorrectly on long lines.
X *	v106: Added -k option and multiple lesskey      3/31/89  mark
X *	      files.
X *	v107: Add 8-bit char support and -g option.	4/27/89  mark
X *	      Split option code into 3 files.
X *	v108: Allocate position table dynamically       5/5/89   mark
X *	      (thanks to Paul Eggert); change % command
X *	      from "percent" to vi-style brace finder.
X *	v109: Added ESC-% command, split prim.c.	5/10/89  mark
X *	v110: Fixed bug in + option; fixed repaint bug	5/24/89  mark
X *	      under Sun windows (thanks to Paul Eggert).
X *	v111: Generalized # and % expansion; use 	5/25/89  mark
X *	      calloc for some error messages.
X *	v112: Get rid of ESC-%, add {}()[] commands.	5/30/89  mark
X *	v113: Optimize lseeks (thanks to Paul Eggert).	5/31/89  mark
X *	v114: Added ESC-/ and ESC-/! commands.		7/25/89  mark
X *	v115: Added ESC-n command.			7/26/89  mark
X *	v116: Added find_pos to optimize g command.	7/31/89  mark
X *	v117: Change -f option to -r.			8/1/89   mark
X *	v118: Save positions for all previous files,	8/2/89   mark
X *	      not just the immediately previous one.
X *	v119: Save marks across file boundaries.	8/7/89   mark
X *	      Add file handle stuff.
X *	v120: Add :ta command.				8/11/89  mark
X *	v121: Add -f option.				8/16/89  mark
X *	v122: Fix performance with many buffers.	8/30/89  mark
X *	v123: Verbose prompts for string options.	8/31/89  mark
X */
X
Xchar version[] = "@(#) less  version 123";
END_OF_FILE