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