home *** CD-ROM | disk | FTP | other *** search
- Subject: v07i083: New release of LESS, Part02/03
- Newsgroups: mod.sources
- Approved: mirror!rs
-
- Submitted by: rgb@nscpdc.uucp (Robert Bond)
- Mod.sources: Volume 7, Issue 83
- Archive-name: less3/Part02
-
- ---- cut here ---- cut here ---- cut here ---- cut here ---- cut here ----
- : This is a shell archive.
- : Unpack by running /bin/sh.
- echo less.nro
- cat >less.nro <<'_SHAR_EOF_'
- .TH LESS l
- .SH NAME
- less \- opposite of more
- .SH SYNOPSIS
- .B "less [-cdepstwmMqQuU] [-h\fIN\fB] [-b[fp]\fIN\fB] [-x\fIN\fB] [-[z]\fIN\fB]"
- .br
- .B " [-P[mM]\fIstring\fB] [-l\fIlogfile\fB] [+\fIcmd\fB] [\fIfilename\fB]..."
- .SH DESCRIPTION
- .I Less
- is a program similar to
- .I more
- (1), but which allows backwards movement
- in the file as well as forward movement.
- Also,
- .I less
- does not have to read the entire input file before starting,
- so with large input files it starts up faster than text editors like
- .I vi
- (1).
- .I Less
- uses termcap, so it can run on a variety of terminals.
- There is even limited support for hardcopy terminals.
- (On a hardcopy terminal, lines which should be printed at the top
- of the screen are prefixed with an up-arrow.)
- .PP
- Commands are based on both
- .I more
- and
- .I vi.
- Commands may be preceeded by a decimal number,
- called N in the descriptions below.
- The number is used by some commands, as indicated.
-
- .SH COMMANDS
- In the following descriptions, ^X means control-X.
- .IP h
- Help: display a summary of these commands.
- If you forget all the other commands, remember this one.
- .PP
- .IP SPACE
- Scroll forward N lines, default one window (see option \-z below).
- If N is more than the screen size, only the final screenful is displayed.
- .PP
- .IP "f or ^F"
- Same as SPACE.
- .PP
- .IP "b or ^B"
- Scroll backward N lines, default one window (see option \-z below).
- If N is more than the screen size, only the final screenful is displayed.
- .PP
- .IP RETURN
- Scroll forward N lines, default 1.
- The entire N lines are displayed, even if N is more than the screen size.
- .PP
- .IP "e or ^E"
- Same as RETURN.
- .PP
- .IP "j or ^J"
- Also the same as RETURN.
- .PP
- .IP "y or ^Y"
- Scroll backward N lines, default 1.
- The entire N lines are displayed, even if N is more than the screen size.
- .IP "k or ^K"
- Same as y.
- .PP
- .IP "d or ^D"
- Scroll forward N lines, default 10.
- If N is specified, it becomes the new default for
- subsequent d and u commands.
- .PP
- .IP "u or ^U"
- Scroll backward N lines, default 10.
- If N is specified, it becomes the new default for
- subsequent d and u commands.
- .PP
- .IP "r or ^R or ^L"
- Repaint the screen.
- .PP
- .IP R
- Repaint the screen, discarding any buffered input.
- Useful if the file is changing while it is being viewed.
- .PP
- .IP g
- Go to line N in the file, default 1 (beginning of file).
- (Warning: this may be slow if N is large.)
- .PP
- .IP G
- Go to line N in the file, default the end of the file.
- (Warning: this may be slow if standard input,
- rather than a file, is being read.)
- .PP
- .IP p
- Go to a position N percent into the file.
- N should be between 0 and 100.
- (This is possible if standard input is being read,
- but only if
- .I less
- has already read to the end of the file.
- It is always fast, but not always useful.)
- .PP
- .IP %
- Same as p.
- .PP
- .IP m
- Followed by any lowercase letter,
- marks the current position with that letter.
- .PP
- .IP "'"
- (Single quote.)
- Followed by any lowercase letter, returns to the position which
- was previously marked with that letter.
- Followed by another single quote, returns to the postion at
- which the last "large" movement command was executed.
- All marks are lost when a new file is examined.
- .PP
- .IP /pattern
- Search forward in the file for the N-th line containing the pattern.
- N defaults to 1.
- The pattern is a regular expression, as recognized by
- .I ed.
- The search starts at the second line displayed
- (but see the \-t option, which changes this).
- .PP
- .IP ?pattern
- Search backward in the file for the N-th line containing the pattern.
- The search starts at the line immediately before the top line displayed.
- .PP
- .IP n
- Repeat previous search, for N-th line containing the last pattern.
- .PP
- .IP E [filename]
- Examine a new file.
- If the filename is missing, the "current" file (see the N and P commands
- below) from the list of files in the command line is re-examined.
- .PP
- .IP N
- Examine the next file (from the list of files given in the command line).
- If a number N is specified (not to be confused with the command N),
- the N-th next file is examined.
- .PP
- .IP P
- Examine the previous file.
- If a number N is specified, the N-th previous file is examined.
- .PP
- .IP "= or ^G"
- Prints some information about the file being viewed,
- including its name
- and the byte offset of the bottom line being displayed.
- If possible, it also prints the length of the file
- and the percent of the file above the last displayed line.
- .PP
- .IP \-
- Followed by one of the command line option letters (see below),
- this will toggle the setting of that option
- and print a message describing the new setting.
- .PP
- .IP +cmd
- Causes the specified cmd to be executed each time a new file is examined.
- For example, +G causes
- .I less
- to initially display each file starting at the end
- rather than the beginning.
- .PP
- .IP V
- Prints the version number of
- .I less
- being run.
- .PP
- .IP q
- Exits
- .I less.
- .PP
- The following
- two
- commands may or may not be valid, depending on your particular installation.
- .PP
- .IP v
- Invokes an editor to edit the current file being viewed.
- The editor is taken from the environment variable EDITOR,
- or defaults to "vi".
- .PP
- .IP "! shell-command"
- Invokes a shell to run the shell-command given.
- .PP
- .SH OPTIONS
- Command line options are described below.
- Most options may be changed while
- .I less
- is running, via the "\-" command.
- .PP
- Options are also taken from the environment variable "LESS".
- For example, if you like
- more-style prompting, to avoid typing "less \-m ..." each time
- .I less
- is invoked, you might tell
- .I csh:
- .sp
- setenv LESS m
- .sp
- or if you use
- .I sh:
- .sp
- LESS=m; export LESS
- .sp
- The environment variable is parsed before the command line,
- so command line options override the LESS environment variable.
- A dollar sign ($) may be used to signal the end of an option string.
- This is important only for options like \-P which take a
- following string.
- .IP \-s
- The \-s option causes
- consecutive blank lines to be squeezed into a single blank line.
- This is useful when viewing
- .I nroff
- output.
- .IP \-t
- Normally, forward searches start just after
- the top displayed line (that is, at the second displayed line).
- Thus forward searches include the currently displayed screen.
- The \-t option causes forward searches to start
- just after the bottom line displayed,
- thus skipping the currently displayed screen.
- .IP \-m
- Normally,
- .I less
- prompts with a colon.
- The \-m option causes
- .I less
- to prompt verbosely (like
- .I more),
- with the percent into the file.
- .IP \-M
- The \-M option causes
- .I less
- to prompt even more verbosely than
- .I more.
- .IP \-P
- The \-P option provides a way to tailor the three prompt
- styles to your own preference.
- You would normally put this option in your LESS environment
- variable, rather than type it in with each less command.
- Such an option must either be the last option in the LESS variable,
- or be terminated by a dollar sign.
- \-P followed by a string changes the default (short) prompt to that string.
- \-Pm changes the medium (\-m) prompt to the string, and
- \-PM changes the long (\-M) prompt.
- The string consists of a sequence of letters which are replaced
- with certain predefined strings, as follows:
- .br
- F file name
- .br
- f file name, only once
- .br
- O file n of n
- .br
- o file n of n, only once
- .br
- b byte offset
- .br
- p percent into file
- .br
- P percent if known, else byte offset
- .br
- Angle brackets, < and >, may be used to surround a
- literal string to be included in the prompt.
- The defaults are "fo" for the short prompt,
- "foP" for the medium prompt, and
- "Fobp" for the long prompt.
- .br
- Example: Setting your LESS variable to "PmFOP$PMFObp"
- would change the medium and long prompts to always
- include the file name and "file n of n" message.
- .br
- Another example: Setting your LESS variable to
- .br
- "mPm<--Less-->FoPe"
- would change the medium prompt to the string "--Less--" followed
- by the file name and percent into the file.
- It also selects the medium
- prompt as the default prompt (because of the first "m").
- .IP \-q
- Normally, if an attempt is made to scroll past the end of the file
- or before the beginning of the file, the terminal bell is rung to
- indicate this fact.
- The \-q option tells
- .I less
- not to ring the bell at such times.
- If the terminal has a "visual bell", it is used instead.
- .IP \-Q
- Even if \-q is given,
- .I less
- will ring the bell on certain other errors,
- such as typing an invalid character.
- The \-Q option tells
- .I less
- to be quiet all the time; that is, never ring the terminal bell.
- If the terminal has a "visual bell", it is used instead.
- .IP \-e
- Normally the only way to exit less is via the "q" command.
- The \-e option tells less to automatically exit
- the second time it reaches end-of-file.
- .IP \-u
- If the \-u option is given,
- backspaces are treated as printable characters;
- that is, they are sent to the terminal when they appear in the input.
- .IP \-U
- If the \-U option is given,
- backspaces are printed as the two character sequence "^H".
- .sp
- If neither \-u nor \-U is given,
- backspaces which appear adjacent to an underscore character
- are treated specially:
- the underlined text is displayed
- using the terminal's hardware underlining capability.
- Also, backspaces which appear between two identical characters
- are treated specially:
- the overstruck text is printed
- using the terminal's hardware boldface capability.
- Other backspaces are deleted, along with the preceeding character.
- .IP \-w
- Normally,
- .I less
- uses a tilde character to represent lines past the end of the file.
- The \-w option causes blank lines to be used instead.
- .IP \-d
- Normally,
- .I less
- will complain if the terminal is dumb; that is, lacks some important capability,
- such as the ability to clear the screen or scroll backwards.
- The \-d option suppresses this complaint
- (but does not otherwise change the behavior of the program on a dumb terminal).
- .IP \-p
- Normally,
- .I less
- will repaint the screen by scrolling from the bottom of the screen.
- If the \-p option is set, when
- .I less
- needs to change the entire display, it will clear the screen
- and paint from the top line down.
- .IP \-h
- Normally,
- .I less
- will scroll backwards when backwards movement is necessary.
- The \-h option specifies a maximum number of lines to scroll backwards.
- If it is necessary to move backwards more than this many lines,
- the screen is repainted in a forward direction.
- (If the terminal does not have the ability to scroll
- backwards, \-h0 is implied.)
- .IP \-[z]
- When given a backwards or forwards window command,
- .I less
- will by
- default scroll backwards or forwards one screenful of lines.
- The \-z\fIn\fR option changes the default scrolling window size
- to \fIn\fR lines.
- If \fIn\fR is greater than the screen size,
- the scrolling window size will be set to one screenful.
- Note that the "z" is optional for compatibility with
- .I more.
- .IP -x
- The -x\fIn\fR option sets tab stops every \fIn\fR positions.
- The default for \fIn\fR is 8.
- .IP -l
- The -l option, followed immediately by a filename,
- will cause
- .I less
- to copy its input to the named file as it is being viewed.
- This applies only when the input file is a pipe,
- not an ordinary file.
- .IP -b
- The -b\fIn\fR option tells
- .I less
- to use a non-standard buffer size.
- There are two standard (default) buffer sizes,
- one is used when a file is being read and the other
- when a pipe (standard input) is being read.
- The current defaults are 5 buffers for files and 12 for pipes.
- (Buffers are 1024 bytes.)
- The number \fIn\fR specifies a different number of buffers to use.
- The -b may be followed by "f", in which case only
- the file default is changed, or by "p" in which case only the
- pipe default is changed. Otherwise, both are changed.
- .IP -c
- Normally, when data is read by
- .I less,
- it is scanned to ensure that bit 7 (the high order bit) is turned off in
- each byte read, and to ensure that there are no null (zero) bytes in
- the data (null bytes are turned into "@" characters).
- If the data is known to be "clean",
- the -c option will tell
- .I less
- to skip this checking, causing an imperceptible speed improvement.
- (However, if the data is not "clean", unpredicatable results may occur.)
- .IP +
- If a command line option begins with \fB+\fR,
- the remainder of that option is taken to be an initial command to
- .I less.
- For example, +G tells
- .I less
- to start at the end of the file rather than the beginning,
- and +/xyz tells it to start at the first occurence of "xyz" in the file.
- As a special case, +<number> acts like +<number>g;
- that is, it starts the display at the specified line number
- (however, see the caveat under the "g" command above).
- If the option starts with \fB++\fR, the initial command applies to
- every file being viewed, not just the first one.
- The + command described previously
- may also be used to set (or change) an initial command for every file.
-
- .SH BUGS
- When used on standard input (rather than a file), you can move
- backwards only a finite amount, corresponding to that portion
- of the file which is still buffered.
- The -b option may be used to expand the buffer space.
- _SHAR_EOF_
-
- echo main.c
- cat >main.c <<'_SHAR_EOF_'
- /*
- * Entry point, initialization, miscellaneous routines.
- */
-
- #include "less.h"
- #include "position.h"
- #include <setjmp.h>
-
- public int ispipe;
- public jmp_buf main_loop;
- public char * first_cmd;
- public char * every_first_cmd;
- public int new_file;
- public int is_tty;
- public char current_file[128];
- public int any_display;
- public int ac;
- public char ** av;
- public int curr_ac;
- #if LOGFILE
- public int logfile = -1;
- public char * namelogfile = NULL;
- #endif
- #if EDITOR
- public char * editor;
- #endif
-
- extern int file;
- extern int nbufs;
- extern int sigs;
- extern int quit_at_eof;
- extern int p_nbufs, f_nbufs;
- extern int back_scroll;
- extern int top_scroll;
- extern int sc_height;
- extern int errmsgs;
-
-
- /*
- * Edit a new file.
- * Filename "-" means standard input.
- * No filename means the "current" file, from the command line.
- */
- public void
- edit(filename)
- char *filename;
- {
- register int f;
- char message[100];
- static didpipe;
-
- if (filename == NULL || *filename == '\0')
- {
- if (curr_ac >= ac)
- {
- error("No current file");
- return;
- }
- filename = av[curr_ac];
- }
- if (strcmp(filename, "-") == 0)
- {
- /*
- * Use standard input.
- */
- if (didpipe)
- {
- error("Can view standard input only once");
- return;
- }
- f = 0;
- } else if ((f = open(filename, 0)) < 0)
- {
- static char co[] = "Cannot open ";
- strcpy(message, co);
- strtcpy(message+sizeof(co)-1, filename,
- sizeof(message)-sizeof(co));
- error(message);
- return;
- }
-
- if (isatty(f))
- {
- /*
- * Not really necessary to call this an error,
- * but if the control terminal (for commands)
- * and the input file (for data) are the same,
- * we get weird results at best.
- */
- error("Can't take input from a terminal");
- if (f > 0)
- close(f);
- return;
- }
-
- #if LOGFILE
- /*
- * If he asked for a log file and we have opened standard input,
- * create the log file.
- * We take care not to blindly overwrite an existing file.
- */
- end_logfile();
- if (f == 0 && namelogfile != NULL && is_tty)
- {
- int exists;
- int answer;
-
- /*
- * {{ We could use access() here. }}
- */
- exists = open(namelogfile, 0);
- close(exists);
- exists = (exists >= 0);
-
- if (exists)
- {
- static char w[] = "WARNING: log file exists: ";
- strcpy(message, w);
- strtcpy(message+sizeof(w)-1, namelogfile,
- sizeof(message)-sizeof(w));
- error(message);
- answer = 'X'; /* Ask the user what to do */
- } else
- answer = 'O'; /* Create the log file */
-
- loop:
- switch (answer)
- {
- case 'O': case 'o':
- logfile = creat(namelogfile, 0644);
- break;
- case 'A': case 'a':
- logfile = open(namelogfile, 1);
- if (lseek(logfile, (off_t)0, 2) < 0)
- {
- close(logfile);
- logfile = -1;
- }
- break;
- case 'D': case 'd':
- answer = 0; /* Don't print an error message */
- break;
- case 'q':
- quit();
- default:
- puts("\n Overwrite, Append, or Don't log? ");
- answer = getc();
- puts("\n");
- flush();
- goto loop;
- }
-
- if (logfile < 0 && answer != 0)
- {
- sprintf(message, "Cannot write to \"%s\"",
- namelogfile);
- error(message);
- }
- }
- #endif
-
- /*
- * We are now committed to using the new file.
- * Close the current input file and set up to use the new one.
- */
- if (file > 0)
- close(file);
- new_file = 1;
- strtcpy(current_file, filename, sizeof(current_file));
- ispipe = (f == 0);
- if (ispipe)
- didpipe = 1;
- file = f;
- ch_init( (ispipe) ? p_nbufs : f_nbufs );
- init_mark();
-
- if (every_first_cmd != NULL)
- first_cmd = every_first_cmd;
-
- if (is_tty)
- {
- int no_display = !any_display;
- any_display = 1;
- if (no_display && errmsgs > 0)
- {
- /*
- * We displayed some messages on error output
- * (file descriptor 2; see error() function).
- * Before erasing the screen contents,
- * display the file name and wait for a keystroke.
- */
- error(filename);
- }
- /*
- * Indicate there is nothing displayed yet.
- */
- pos_clear();
- }
- }
-
- /*
- * Edit the next file in the command line list.
- */
- public void
- next_file(n)
- int n;
- {
- if (curr_ac + n >= ac)
- {
- if (quit_at_eof)
- quit();
- error("No (N-th) next file");
- } else
- edit(av[curr_ac += n]);
- }
-
- /*
- * Edit the previous file in the command line list.
- */
- public void
- prev_file(n)
- int n;
- {
- if (curr_ac - n < 0)
- error("No (N-th) previous file");
- else
- edit(av[curr_ac -= n]);
- }
-
- /*
- * Copy a file directly to standard output.
- * Used if standard output is not a tty.
- */
- static void
- cat_file()
- {
- register int c;
-
- while ((c = ch_forw_get()) != EOF)
- putc(c);
- flush();
- }
-
- /*
- * Entry point.
- */
- main(argc, argv)
- int argc;
- char *argv[];
- {
- char *getenv();
-
-
- /*
- * Process command line arguments and LESS environment arguments.
- * Command line arguments override environment arguments.
- */
- init_option();
- scan_option(getenv("LESS"));
- argv++;
- while ( (--argc > 0) &&
- (argv[0][0] == '-' || argv[0][0] == '+') &&
- argv[0][1] != '\0')
- scan_option(*argv++);
-
- #if EDITOR
- editor = getenv("EDITOR");
- if (editor == NULL || *editor == '\0')
- editor = EDIT_PGM;
- #endif
-
- /*
- * Set up list of files to be examined.
- */
- ac = argc;
- av = argv;
- curr_ac = 0;
-
- /*
- * Set up terminal, etc.
- */
- is_tty = isatty(1);
- if (!is_tty)
- {
- /*
- * Output is not a tty.
- * Just copy the input file(s) to output.
- */
- if (ac < 1)
- {
- edit("-");
- cat_file();
- } else
- {
- do
- {
- edit((char *)NULL);
- if (file >= 0)
- cat_file();
- } while (++curr_ac < ac);
- }
- exit(0);
- }
-
- raw_mode(1);
- get_term();
- open_getc();
- init();
-
- if (setjmp(main_loop))
- quit();
- init_signals();
-
- /*
- * Select the first file to examine.
- */
- if (ac < 1)
- edit("-"); /* Standard input */
- else
- {
- /*
- * Try all the files named as command arguments.
- * We are simply looking for one which can be
- * opened without error.
- */
- do
- {
- edit((char *)NULL);
- } while (file < 0 && ++curr_ac < ac);
- }
-
- if (file >= 0)
- commands();
- quit();
- }
-
- /*
- * Copy a string, truncating to the specified length if necessary.
- * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
- */
- strtcpy(to, from, len)
- char *to;
- char *from;
- int len;
- {
- strncpy(to, from, len);
- to[len-1] = '\0';
- }
-
- /*
- * Exit the program.
- */
- public void
- quit()
- {
- /*
- * Put cursor at bottom left corner, clear the line,
- * reset the terminal modes, and exit.
- */
- #if LOGFILE
- end_logfile();
- #endif
- lower_left();
- clear_eol();
- deinit();
- flush();
- raw_mode(0);
- exit(0);
- }
- _SHAR_EOF_
-
- echo option.c
- cat >option.c <<'_SHAR_EOF_'
- /*
- * Process command line options.
- * Each option is a single letter which controls a program variable.
- * The options have defaults which may be changed via
- * the command line option, or toggled via the "-" command.
- */
-
- #include "less.h"
-
- #define toupper(c) ((c)-'a'+'A')
-
- #define END_OPTION_STRING ('$')
-
- /*
- * Types of options.
- */
- #define BOOL 01 /* Boolean option: 0 or 1 */
- #define TRIPLE 02 /* Triple-valued option: 0, 1 or 2 */
- #define NUMBER 04 /* Numeric option */
- #define REPAINT 040 /* Repaint screen after toggling option */
- #define NO_TOGGLE 0100 /* Option cannot be toggled with "-" cmd */
-
- /*
- * Variables controlled by command line options.
- */
- public int p_nbufs, f_nbufs; /* Number of buffers. There are two values,
- one used for input from a pipe and
- the other for input from a file. */
- public int clean_data; /* Can we assume the data is "clean"?
- (That is, free of nulls, etc) */
- public int quiet; /* Should we suppress the audible bell? */
- public int top_search; /* Should forward searches start at the top
- of the screen? (alternative is bottom) */
- public int top_scroll; /* Repaint screen from top?
- (alternative is scroll from bottom) */
- public int pr_type; /* Type of prompt (short, medium, long) */
- public int bs_mode; /* How to process backspaces */
- public int know_dumb; /* Don't complain about dumb terminals */
- public int quit_at_eof; /* Quit after hitting end of file twice */
- public int squeeze; /* Squeeze multiple blank lines into one */
- public int tabstop; /* Tab settings */
- public int back_scroll; /* Repaint screen on backwards movement */
- public int twiddle; /* Display "~" for lines after EOF */
-
- extern char *prproto[];
- extern int nbufs;
- extern int sc_window;
- extern char *first_cmd;
- extern char *every_first_cmd;
- #if LOGFILE
- extern char *namelogfile;
- #endif
-
- #define DEF_F_NBUFS 5 /* Default for f_nbufs */
- #define DEF_P_NBUFS 12 /* Default for p_nbufs */
-
- static struct option
- {
- char oletter; /* The controlling letter (a-z) */
- char otype; /* Type of the option */
- int odefault; /* Default value */
- int *ovar; /* Pointer to the associated variable */
- char *odesc[3]; /* Description of each value */
- } option[] =
- {
- { 'c', BOOL, 0, &clean_data,
- { "Don't assume data is clean",
- "Assume data is clean",
- NULL
- }
- },
- { 'd', BOOL|NO_TOGGLE, 0, &know_dumb,
- { NULL, NULL, NULL}
- },
- { 'e', BOOL, 0, &quit_at_eof,
- { "Don't quit at end-of-file",
- "Quit at end-of-file",
- NULL
- }
- },
- { 'h', NUMBER, -1, &back_scroll,
- { "Backwards scroll limit is %d lines",
- NULL, NULL
- }
- },
- { 'p', BOOL, 0, &top_scroll,
- { "Repaint by scrolling from bottom of screen",
- "Repaint by painting from top of screen",
- NULL
- }
- },
- { 'x', NUMBER, 8, &tabstop,
- { "Tab stops every %d spaces",
- NULL, NULL
- }
- },
- { 's', BOOL|REPAINT, 0, &squeeze,
- { "Don't squeeze multiple blank lines",
- "Squeeze multiple blank lines",
- NULL
- }
- },
- { 't', BOOL, 1, &top_search,
- { "Forward search starts from bottom of screen",
- "Forward search starts from top of screen",
- NULL
- }
- },
- { 'w', BOOL|REPAINT, 1, &twiddle,
- { "Display nothing for lines after end-of-file",
- "Display ~ for lines after end-of-file",
- NULL
- }
- },
- { 'm', TRIPLE, 0, &pr_type,
- { "Short prompt",
- "Medium prompt",
- "Long prompt"
- }
- },
- { 'q', TRIPLE, 0, &quiet,
- { "Ring the bell for errors AND at eof/bof",
- "Ring the bell for errors but not at eof/bof",
- "Never ring the bell"
- }
- },
- { 'u', TRIPLE|REPAINT, 0, &bs_mode,
- { "Underlined text displayed in underline mode",
- "Backspaces cause overstrike",
- "Backspaces print as ^H"
- }
- },
- { 'z', NUMBER, 24, &sc_window,
- { "Scroll window size is %d lines",
- NULL, NULL
- }
- },
- { '\0' }
- };
-
- public char all_options[64]; /* List of all valid options */
-
- /*
- * Initialize each option to its default value.
- */
- public void
- init_option()
- {
- register struct option *o;
- register char *p;
-
- /*
- * First do special cases, not in option table.
- */
- first_cmd = every_first_cmd = NULL;
- f_nbufs = DEF_F_NBUFS; /* -bf */
- p_nbufs = DEF_P_NBUFS; /* -bp */
-
- p = all_options;
- *p++ = 'b';
-
- for (o = option; o->oletter != '\0'; o++)
- {
- /*
- * Set each variable to its default.
- * Also make a list of all options, in "all_options".
- */
- *(o->ovar) = o->odefault;
- *p++ = o->oletter;
- if (o->otype & TRIPLE)
- *p++ = toupper(o->oletter);
- }
- *p = '\0';
- }
-
- /*
- * Toggle command line flags from within the program.
- * Used by the "-" command.
- */
- public void
- toggle_option(c)
- int c;
- {
- register struct option *o;
- char message[100];
- char buf[5];
-
- /*
- * First check for special cases not handled by the option table.
- */
- switch (c)
- {
- case 'b':
- sprintf(message, "%d buffers", nbufs);
- error(message);
- return;
- }
-
-
- for (o = option; o->oletter != '\0'; o++)
- {
- if (o->otype & NO_TOGGLE)
- continue;
- if ((o->otype & BOOL) && (o->oletter == c))
- {
- /*
- * Boolean option:
- * just toggle it.
- */
- *(o->ovar) = ! *(o->ovar);
- } else if ((o->otype & TRIPLE) && (o->oletter == c))
- {
- /*
- * Triple-valued option with lower case letter:
- * make it 1 unless already 1, then make it 0.
- */
- *(o->ovar) = (*(o->ovar) == 1) ? 0 : 1;
- } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c))
- {
- /*
- * Triple-valued option with upper case letter:
- * make it 2 unless already 2, then make it 0.
- */
- *(o->ovar) = (*(o->ovar) == 2) ? 0 : 2;
- } else if ((o->otype & NUMBER) && (o->oletter == c))
- {
- sprintf(message, o->odesc[0],
- (o->ovar == &back_scroll) ?
- get_back_scroll() : *(o->ovar));
- error(message);
- return;
- } else
- continue;
-
- if (o->otype & REPAINT)
- repaint();
- error(o->odesc[*(o->ovar)]);
- return;
- }
-
- if (control_char(c))
- sprintf(buf, "^%c", carat_char(c));
- else
- sprintf(buf, "%c", c);
- sprintf(message, "\"-%s\": no such flag. Use one of \"%s\"",
- buf, all_options);
- error(message);
- }
-
- /*
- * Scan to end of string or to an END_OPTION_STRING character.
- * In the latter case, replace the char with a null char.
- * Return a pointer to the remainder of the string, if any.
- */
- static char *
- optstring(s)
- char *s;
- {
- register char *p;
-
- for (p = s; *p != '\0'; p++)
- if (*p == END_OPTION_STRING)
- {
- *p = '\0';
- return (p+1);
- }
- return (p);
- }
-
- /*
- * Scan an argument (either from command line or from LESS environment
- * variable) and process it.
- */
- public void
- scan_option(s)
- char *s;
- {
- register struct option *o;
- register int c;
- char message[80];
-
- if (s == NULL)
- return;
-
- next:
- if (*s == '\0')
- return;
- switch (c = *s++)
- {
- case '-':
- case ' ':
- case '\t':
- case END_OPTION_STRING:
- goto next;
- case '+':
- if (*s == '+')
- every_first_cmd = ++s;
- first_cmd = s;
- s = optstring(s);
- goto next;
- case 'P':
- switch (*s)
- {
- case 'm': prproto[PR_MEDIUM] = ++s; break;
- case 'M': prproto[PR_LONG] = ++s; break;
- default: prproto[PR_SHORT] = s; break;
- }
- s = optstring(s);
- goto next;
- #if LOGFILE
- case 'l':
- namelogfile = s;
- s = optstring(s);
- goto next;
- #endif
- case 'b':
- switch (*s)
- {
- case 'f':
- s++;
- f_nbufs = getnum(&s, 'b');
- break;
- case 'p':
- s++;
- p_nbufs = getnum(&s, 'b');
- break;
- default:
- f_nbufs = p_nbufs = getnum(&s, 'b');
- break;
- }
- goto next;
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- {
- /*
- * Handle special "more" compatibility form "-number"
- * to set the scrolling window size.
- */
- s--;
- sc_window = getnum(&s, '-');
- goto next;
- }
- }
-
- for (o = option; o->oletter != '\0'; o++)
- {
- if ((o->otype & BOOL) && (o->oletter == c))
- {
- *(o->ovar) = ! o->odefault;
- goto next;
- } else if ((o->otype & TRIPLE) && (o->oletter == c))
- {
- *(o->ovar) = (o->odefault == 1) ? 0 : 1;
- goto next;
- } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c))
- {
- *(o->ovar) = (o->odefault == 2) ? 0 : 2;
- goto next;
- } else if ((o->otype & NUMBER) && (o->oletter == c))
- {
- *(o->ovar) = getnum(&s, c);
- goto next;
- }
- }
-
- sprintf(message, "\"-%c\": invalid flag", c);
- error(message);
- exit(1);
- }
-
- /*
- * Translate a string into a number.
- * Like atoi(), but takes a pointer to a char *, and updates
- * the char * to point after the translated number.
- */
- static int
- getnum(sp, c)
- char **sp;
- int c;
- {
- register char *s;
- register int n;
- char message[80];
-
- s = *sp;
- if (*s < '0' || *s > '9')
- {
- sprintf(message, "number is required after -%c", c);
- error(message);
- exit(1);
- }
-
- n = 0;
- while (*s >= '0' && *s <= '9')
- n = 10 * n + *s++ - '0';
- *sp = s;
- return (n);
- }
- _SHAR_EOF_
-
- echo prim.c
- cat >prim.c <<'_SHAR_EOF_'
- /*
- * Primitives for displaying the file on the screen.
- */
-
- #include "less.h"
- #include "position.h"
-
- public int hit_eof; /* Keeps track of how many times we hit end of file */
-
- extern int quiet;
- extern int top_search;
- extern int top_scroll;
- extern int back_scroll;
- extern int sc_width, sc_height;
- extern int sigs;
- extern char *line;
- extern char *first_cmd;
-
- /*
- * Sound the bell to indicate he is trying to move past end of file.
- */
- static void
- eof_bell()
- {
- if (quiet == NOT_QUIET)
- bell();
- else
- vbell();
- }
-
- /*
- * Check to see if the end of file is currently "displayed".
- */
- static void
- eof_check()
- {
- POSITION pos;
-
- /*
- * If the bottom line is empty, we are at EOF.
- * If the bottom line ends at the file length,
- * we must be just at EOF.
- */
- pos = position(BOTTOM_PLUS_ONE);
- if (pos == NULL_POSITION || pos == ch_length())
- hit_eof++;
- }
-
- /*
- * Display n lines, scrolling forward,
- * starting at position pos in the input file.
- * "force" means display the n lines even if we hit end of file.
- * "only_last" means display only the last screenful if n > screen size.
- */
- static void
- forw(n, pos, force, only_last)
- register int n;
- POSITION pos;
- int force;
- int only_last;
- {
- int eof = 0;
- int nlines = 0;
- int repaint_flag;
- static int first_time = 1;
-
- /*
- * repaint_flag tells us not to display anything till the end,
- * then just repaint the entire screen.
- */
- repaint_flag = (only_last && n > sc_height-1);
-
- if (!repaint_flag)
- {
- if (top_scroll && n >= sc_height - 1)
- {
- /*
- * Start a new screen.
- * {{ This is not really desirable if we happen
- * to hit eof in the middle of this screen,
- * but we don't yet know if that will happen. }}
- */
- clear();
- home();
- force = 1;
- } else
- {
- lower_left();
- clear_eol();
- }
-
- if (pos != position(BOTTOM_PLUS_ONE))
- {
- /*
- * This is not contiguous with what is
- * currently displayed. Clear the screen image
- * (position table) and start a new screen.
- */
- pos_clear();
- add_forw_pos(pos);
- force = 1;
- if (top_scroll)
- {
- clear();
- home();
- } else if (!first_time)
- {
- puts("...skipping...\n");
- }
- }
- }
-
- while (--n >= 0)
- {
- /*
- * Read the next line of input.
- */
- pos = forw_line(pos);
- if (pos == NULL_POSITION)
- {
- /*
- * End of file: stop here unless the top line
- * is still empty, or "force" is true.
- */
- eof = 1;
- if (!force && position(TOP) != NULL_POSITION)
- break;
- line = NULL;
- }
- /*
- * Add the position of the next line to the position table.
- * Display the current line on the screen.
- */
- add_forw_pos(pos);
- nlines++;
- if (repaint_flag ||
- (first_time && line == NULL && !top_scroll))
- continue;
- put_line();
- }
-
- if (eof)
- hit_eof++;
- else
- eof_check();
- if (nlines == 0)
- eof_bell();
- else if (repaint_flag)
- repaint();
- first_time = 0;
- }
-
- /*
- * Display n lines, scrolling backward.
- */
- static void
- back(n, pos, force, only_last)
- register int n;
- POSITION pos;
- int force;
- int only_last;
- {
- int nlines = 0;
- int repaint_flag;
-
- repaint_flag = (n > get_back_scroll() || (only_last && n > sc_height-1));
- hit_eof = 0;
- while (--n >= 0)
- {
- /*
- * Get the previous line of input.
- */
- pos = back_line(pos);
- if (pos == NULL_POSITION)
- {
- /*
- * Beginning of file: stop here unless "force" is true.
- */
- if (!force)
- break;
- line = NULL;
- }
- /*
- * Add the position of the previous line to the position table.
- * Display the line on the screen.
- */
- add_back_pos(pos);
- nlines++;
- if (!repaint_flag)
- {
- home();
- add_line();
- put_line();
- }
- }
-
- eof_check();
- if (nlines == 0)
- eof_bell();
- else if (repaint_flag)
- repaint();
- }
-
- /*
- * Display n more lines, forward.
- * Start just after the line currently displayed at the bottom of the screen.
- */
- public void
- forward(n, only_last)
- int n;
- int only_last;
- {
- POSITION pos;
-
- pos = position(BOTTOM_PLUS_ONE);
- if (pos == NULL_POSITION)
- {
- eof_bell();
- hit_eof++;
- return;
- }
- forw(n, pos, 0, only_last);
- }
-
- /*
- * Display n more lines, backward.
- * Start just before the line currently displayed at the top of the screen.
- */
- public void
- backward(n, only_last)
- int n;
- int only_last;
- {
- POSITION pos;
-
- pos = position(TOP);
- if (pos == NULL_POSITION)
- {
- /*
- * This will almost never happen,
- * because the top line is almost never empty.
- */
- eof_bell();
- return;
- }
- back(n, pos, 0, only_last);
- }
-
- /*
- * Repaint the screen, starting from a specified position.
- */
- static void
- prepaint(pos)
- POSITION pos;
- {
- hit_eof = 0;
- forw(sc_height-1, pos, 0, 0);
- }
-
- /*
- * Repaint the screen.
- */
- public void
- repaint()
- {
- /*
- * Start at the line currently at the top of the screen
- * and redisplay the screen.
- */
- prepaint(position(TOP));
- }
-
- /*
- * Jump to the end of the file.
- * It is more convenient to paint the screen backward,
- * from the end of the file toward the beginning.
- */
- public void
- jump_forw()
- {
- POSITION pos;
-
- if (ch_end_seek())
- {
- error("Cannot seek to end of file");
- return;
- }
- lastmark();
- pos = ch_tell();
- clear();
- pos_clear();
- add_back_pos(pos);
- back(sc_height - 1, pos, 0, 0);
- }
-
- /*
- * Jump to line n in the file.
- */
- public void
- jump_back(n)
- register int n;
- {
- register int c;
- int nlines;
-
- /*
- * This is done the slow way, by starting at the beginning
- * of the file and counting newlines.
- */
- if (ch_seek((POSITION)0))
- {
- /*
- * Probably a pipe with beginning of file no longer buffered.
- * If he wants to go to line 1, we do the best we can,
- * by going to the first line which is still buffered.
- */
- if (n <= 1 && ch_beg_seek() == 0)
- jump_loc(ch_tell());
- error("Cannot get to beginning of file");
- return;
- }
-
- /*
- * Start counting lines.
- */
- for (nlines = 1; nlines < n; nlines++)
- {
- while ((c = ch_forw_get()) != '\n')
- if (c == EOF)
- {
- char message[40];
- sprintf(message, "File has only %d lines",
- nlines-1);
- error(message);
- return;
- }
- }
-
- jump_loc(ch_tell());
- }
-
- /*
- * Jump to a specified percentage into the file.
- * This is a poor compensation for not being able to
- * quickly jump to a specific line number.
- */
- public void
- jump_percent(percent)
- int percent;
- {
- POSITION pos, len;
- register int c;
-
- /*
- * Determine the position in the file
- * (the specified percentage of the file's length).
- */
- if ((len = ch_length()) == NULL_POSITION)
- {
- error("Don't know length of file");
- return;
- }
- pos = (percent * len) / 100;
-
- /*
- * Back up to the beginning of the line.
- */
- if (ch_seek(pos) == 0)
- {
- while ((c = ch_back_get()) != '\n' && c != EOF)
- ;
- if (c == '\n')
- (void) ch_forw_get();
- pos = ch_tell();
- }
- jump_loc(pos);
- }
-
- /*
- * Jump to a specified position in the file.
- */
- public void
- jump_loc(pos)
- POSITION pos;
- {
- register int nline;
- POSITION tpos;
-
- /*
- * See if the desired line is BEFORE the currently
- * displayed screen. If so, see if it is close enough
- * to scroll backwards to it.
- * {{ This can be expensive if he has specified a very
- * large back_scroll count. Perhaps we should put
- * some sanity limit on the loop count here. }}
- */
- tpos = position(TOP);
- if (tpos != NULL_POSITION && pos < tpos)
- {
- int bs = get_back_scroll();
- for (nline = 1; nline <= bs; nline++)
- {
- tpos = back_line(tpos);
- if (tpos == NULL_POSITION)
- break;
- if (tpos <= pos)
- {
- back(nline, position(TOP), 1, 0);
- return;
- }
- }
- } else if ((nline = onscreen(pos)) >= 0)
- {
- /*
- * The line is currently displayed.
- * Just scroll there.
- */
- forw(nline, position(BOTTOM_PLUS_ONE), 1, 0);
- return;
- }
-
- /*
- * Line is not on screen.
- * Remember where we were; clear and paint the screen.
- */
- if (ch_seek(pos))
- {
- error("Cannot seek to that position");
- return;
- }
- lastmark();
- prepaint(pos);
- }
-
- /*
- * The table of marks.
- * A mark is simply a position in the file.
- */
- #define NMARKS (27) /* 26 for a-z plus one for quote */
- #define LASTMARK (NMARKS-1) /* For quote */
- static POSITION marks[NMARKS];
-
- /*
- * Initialize the mark table to show no marks are set.
- */
- public void
- init_mark()
- {
- int i;
-
- for (i = 0; i < NMARKS; i++)
- marks[i] = NULL_POSITION;
- }
-
- /*
- * See if a mark letter is valid (between a and z).
- */
- static int
- badmark(c)
- int c;
- {
- if (c < 'a' || c > 'z')
- {
- error("Choose a letter between 'a' and 'z'");
- return (1);
- }
- return (0);
- }
-
- /*
- * Set a mark.
- */
- public void
- setmark(c)
- int c;
- {
- if (badmark(c))
- return;
- marks[c-'a'] = position(TOP);
- }
-
- public void
- lastmark()
- {
- marks[LASTMARK] = position(TOP);
- }
-
- /*
- * Go to a previously set mark.
- */
- public void
- gomark(c)
- int c;
- {
- POSITION pos;
-
- if (c == '\'')
- pos = marks[LASTMARK];
- else if (badmark(c))
- return;
- else
- pos = marks[c-'a'];
-
- if (pos == NULL_POSITION)
- error("mark not set");
- else
- jump_loc(pos);
- }
-
- /*
- * Get the backwards scroll limit.
- * Must call this function instead of just using the value of
- * back_scroll, because the default case depends on sc_height and
- * top_scroll, as well as back_scroll.
- */
- public int
- get_back_scroll()
- {
- if (back_scroll < 0)
- return (sc_height - 1 - top_scroll);
- return (back_scroll);
- }
-
- /*
- * Search for the n-th occurence of a specified pattern,
- * either forward (direction == '/'), or backwards (direction == '?').
- */
- public void
- search(direction, pattern, n)
- int direction;
- char *pattern;
- register int n;
- {
- register int search_forward = (direction == '/');
- POSITION pos, linepos;
-
- #if RECOMP
- char *re_comp();
- char *errmsg;
-
- /*
- * (re_comp handles a null pattern internally,
- * so there is no need to check for a null pattern here.)
- */
- if ((errmsg = re_comp(pattern)) != NULL)
- {
- error(errmsg);
- return;
- }
- #else
- #if REGCMP
- char *regcmp();
- static char *cpattern = NULL;
-
- if (pattern == NULL || *pattern == '\0')
- {
- /*
- * A null pattern means use the previous pattern.
- * The compiled previous pattern is in cpattern, so just use it.
- */
- if (cpattern == NULL)
- {
- error("No previous regular expression");
- return;
- }
- } else
- {
- /*
- * Otherwise compile the given pattern.
- */
- char *s;
- if ((s = regcmp(pattern, 0)) == NULL)
- {
- error("Invalid pattern");
- return;
- }
- if (cpattern != NULL)
- free(cpattern);
- cpattern = s;
- }
- #else
- static char lpbuf[100];
- static char *last_pattern = NULL;
-
- if (pattern == NULL || *pattern == '\0')
- {
- /*
- * Null pattern means use the previous pattern.
- */
- if (last_pattern == NULL)
- {
- error("No previous regular expression");
- return;
- }
- pattern = last_pattern;
- } else
- {
- strcpy(lpbuf, pattern);
- last_pattern = lpbuf;
- }
- #endif
- #endif
-
- /*
- * Figure out where to start the search.
- */
-
- if (position(TOP) == NULL_POSITION)
- {
- /*
- * Nothing is currently displayed.
- * Start at the beginning of the file.
- * (This case is mainly for first_cmd searches,
- * for example, "+/xyz" on the command line.)
- */
- pos = (POSITION)0;
- } else if (!search_forward)
- {
- /*
- * Backward search: start just before the top line
- * displayed on the screen.
- */
- pos = position(TOP);
- } else if (top_search)
- {
- /*
- * Forward search and "start from top".
- * Start at the second line displayed on the screen.
- */
- pos = position(TOP_PLUS_ONE);
- } else
- {
- /*
- * Forward search but don't "start from top".
- * Start just after the bottom line displayed on the screen.
- */
- pos = position(BOTTOM_PLUS_ONE);
- }
-
- if (pos == NULL_POSITION)
- {
- /*
- * Can't find anyplace to start searching from.
- */
- error("Nothing to search");
- return;
- }
-
- for (;;)
- {
- /*
- * Get lines until we find a matching one or
- * until we hit end-of-file (or beginning-of-file
- * if we're going backwards).
- */
- if (sigs)
- /*
- * A signal aborts the search.
- */
- return;
-
- if (search_forward)
- {
- /*
- * Read the next line, and save the
- * starting position of that line in linepos.
- */
- linepos = pos;
- pos = forw_raw_line(pos);
- } else
- {
- /*
- * Read the previous line and save the
- * starting position of that line in linepos.
- */
- pos = back_raw_line(pos);
- linepos = pos;
- }
-
- if (pos == NULL_POSITION)
- {
- /*
- * We hit EOF/BOF without a match.
- */
- error("Pattern not found");
- return;
- }
-
- /*
- * Test the next line to see if we have a match.
- * This is done in a variety of ways, depending
- * on what pattern matching functions are available.
- */
- #if REGCMP
- if ( (regex(cpattern, line) != NULL)
- #else
- #if RECOMP
- if ( (re_exec(line) == 1)
- #else
- if ( (match(pattern, line))
- #endif
- #endif
- && (--n <= 0) )
- /*
- * Found the matching line.
- */
- break;
- }
-
- jump_loc(linepos);
- }
-
- #if (!REGCMP) && (!RECOMP)
- /*
- * We have neither regcmp() nor re_comp().
- * We use this function to do simple pattern matching.
- * It supports no metacharacters like *, etc.
- */
- static int
- match(pattern, buf)
- char *pattern, *buf;
- {
- register char *pp, *lp;
-
- for ( ; *buf != '\0'; buf++)
- {
- for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++)
- if (*pp == '\0' || *lp == '\0')
- break;
- if (*pp == '\0')
- return (1);
- }
- return (0);
- }
- #endif
- _SHAR_EOF_
-
- echo ch.c
- cat >ch.c <<'_SHAR_EOF_'
- /*
- * Low level character input from the input file.
- * We use these special purpose routines which optimize moving
- * both forward and backward from the current read pointer.
- */
-
- #include "less.h"
-
- public int file = -1; /* File descriptor of the input file */
-
- /*
- * Pool of buffers holding the most recently used blocks of the input file.
- */
- #define BUFSIZ 1024
- struct buf {
- struct buf *next, *prev;
- long block;
- char data[BUFSIZ];
- };
- static struct buf *bufs = NULL;
- public int nbufs;
-
- /*
- * The buffer pool is kept as a doubly-linked circular list,
- * in order from most- to least-recently used.
- * The circular list is anchored by buf_anchor.
- */
- static struct {
- struct buf *next, *prev;
- } buf_anchor;
- #define END_OF_CHAIN ((struct buf *)&buf_anchor)
- #define buf_head buf_anchor.next
- #define buf_tail buf_anchor.prev
-
- /*
- * If we fail to allocate enough memory for buffers, we try to limp
- * along with a minimum number of buffers.
- */
- #define DEF_NBUFS 2 /* Minimum number of buffers */
-
- extern int clean_data;
- extern int ispipe;
- extern int sigs;
-
- #if LOGFILE
- extern int logfile;
- #endif
-
- /*
- * Current position in file.
- * Stored as a block number and an offset into the block.
- */
- static long ch_block;
- static int ch_offset;
-
- /*
- * Length of file, needed if input is a pipe.
- */
- static POSITION ch_fsize;
-
- /*
- * Largest block number read if input is standard input (a pipe).
- */
- static long last_piped_block;
-
- /*
- * Get the character pointed to by the read pointer.
- * ch_get() is a macro which is more efficient to call
- * than fch_get (the function), in the usual case
- * that the block desired is at the head of the chain.
- */
- #define ch_get() ((buf_head->block == ch_block) ? \
- buf_head->data[ch_offset] : fch_get())
- static int
- fch_get()
- {
- register struct buf *bp;
- register int n;
- register int end;
- POSITION pos;
-
- /*
- * Look for a buffer holding the desired block.
- */
- for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
- if (bp->block == ch_block)
- goto found;
- /*
- * Block is not in a buffer.
- * Take the least recently used buffer
- * and read the desired block into it.
- */
- bp = buf_tail;
- bp->block = ch_block;
- pos = ch_block * BUFSIZ;
- if (ispipe)
- {
- /*
- * The block requested should be one more than
- * the last block read.
- */
- if (ch_block != ++last_piped_block)
- {
- /* This "should not happen". */
- char message[80];
- sprintf(message, "Pipe error: last %ld, want %ld\n",
- (long)last_piped_block-1, (long)ch_block);
- error(message);
- quit();
- }
- } else
- lseek(file, pos, 0);
-
- /*
- * Read the block. This may take several reads if the input
- * is coming from standard input, due to the nature of pipes.
- */
- end = 0;
- while ((n = read(file, &bp->data[end], BUFSIZ-end)) > 0)
- if ((end += n) >= BUFSIZ)
- break;
-
- if (n < 0)
- {
- error("read error");
- quit();
- }
-
- #if LOGFILE
- /*
- * If we have a log file, write this block to it.
- */
- if (logfile >= 0 && end > 0)
- write(logfile, bp->data, end);
- #endif
-
- /*
- * Set an EOF marker in the buffered data itself.
- * Then ensure the data is "clean": there are no
- * extra EOF chars in the data and that the "meta"
- * bit (the 0200 bit) is reset in each char.
- */
- if (end < BUFSIZ)
- {
- ch_fsize = pos + end;
- bp->data[end] = EOF;
- }
-
- if (!clean_data)
- while (--end >= 0)
- {
- bp->data[end] &= 0177;
- if (bp->data[end] == EOF)
- bp->data[end] = '@';
- }
-
- found:
- /* if (buf_head != bp) {this is guaranteed by the ch_get macro} */
- {
- /*
- * Move the buffer to the head of the buffer chain.
- * This orders the buffer chain, most- to least-recently used.
- */
- bp->next->prev = bp->prev;
- bp->prev->next = bp->next;
-
- bp->next = buf_head;
- bp->prev = END_OF_CHAIN;
- buf_head->prev = bp;
- buf_head = bp;
- }
- return (bp->data[ch_offset]);
- }
-
- #if LOGFILE
- /*
- * Close the logfile.
- * If we haven't read all of standard input into it, do that now.
- */
- public void
- end_logfile()
- {
- static int tried;
-
- if (logfile < 0)
- return;
- if (!tried && ch_fsize == NULL_POSITION)
- {
- tried = 1;
- lower_left();
- clear_eol();
- so_enter();
- puts("finishing logfile... (interrupt to abort)");
- so_exit();
- flush();
- while (sigs == 0 && ch_forw_get() != EOF)
- ;
- }
- close(logfile);
- logfile = -1;
- }
- #endif
-
- /*
- * Determine if a specific block is currently in one of the buffers.
- */
- static int
- buffered(block)
- long block;
- {
- register struct buf *bp;
-
- for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
- if (bp->block == block)
- return (1);
- return (0);
- }
-
- /*
- * Seek to a specified position in the file.
- * Return 0 if successful, non-zero if can't seek there.
- */
- public int
- ch_seek(pos)
- register POSITION pos;
- {
- long new_block;
-
- new_block = pos / BUFSIZ;
- if (!ispipe || new_block == last_piped_block + 1 || buffered(new_block))
- {
- /*
- * Set read pointer.
- */
- ch_block = new_block;
- ch_offset = pos % BUFSIZ;
- return (0);
- }
- return (1);
- }
-
- /*
- * Seek to the end of the file.
- */
- public int
- ch_end_seek()
- {
- if (ispipe)
- {
- /*
- * Do it the slow way: read till end of data.
- */
- while (ch_forw_get() != EOF)
- ;
- } else
- {
- (void) ch_seek((POSITION)(lseek(file, (off_t)0, 2)));
- }
- return (0);
- }
-
- /*
- * Seek to the beginning of the file, or as close to it as we can get.
- * We may not be able to seek there if input is a pipe and the
- * beginning of the pipe is no longer buffered.
- */
- public int
- ch_beg_seek()
- {
- register struct buf *bp, *firstbp;
-
- /*
- * Try a plain ch_seek first.
- */
- if (ch_seek((POSITION)0) == 0)
- return (0);
-
- /*
- * Can't get to position 0.
- * Look thru the buffers for the one closest to position 0.
- */
- firstbp = bp = buf_head;
- if (bp == END_OF_CHAIN)
- return (1);
- while ((bp = bp->next) != END_OF_CHAIN)
- if (bp->block < firstbp->block)
- firstbp = bp;
- ch_block = firstbp->block;
- ch_offset = 0;
- return (0);
- }
-
- /*
- * Return the length of the file, if known.
- */
- public POSITION
- ch_length()
- {
- if (ispipe)
- return (ch_fsize);
- return ((POSITION)(lseek(file, (off_t)0, 2)));
- }
-
- /*
- * Return the current position in the file.
- */
- public POSITION
- ch_tell()
- {
- return (ch_block * BUFSIZ + ch_offset);
- }
-
- /*
- * Get the current char and post-increment the read pointer.
- */
- public int
- ch_forw_get()
- {
- register int c;
-
- c = ch_get();
- if (c != EOF && ++ch_offset >= BUFSIZ)
- {
- ch_offset = 0;
- ch_block ++;
- }
- return (c);
- }
-
- /*
- * Pre-decrement the read pointer and get the new current char.
- */
- public int
- ch_back_get()
- {
- register int c;
-
- if (--ch_offset < 0)
- {
- if (ch_block <= 0 || (ispipe && !buffered(ch_block-1)))
- {
- ch_offset = 0;
- return (EOF);
- }
- ch_offset = BUFSIZ - 1;
- ch_block--;
- }
- c = ch_get();
- return (c);
- }
-
- /*
- * Initialize the buffer pool to all empty.
- * Caller suggests that we use want_nbufs buffers.
- */
- public void
- ch_init(want_nbufs)
- int want_nbufs;
- {
- register struct buf *bp;
- char *calloc();
-
- if (nbufs < want_nbufs)
- {
- /*
- * We don't have enough buffers.
- * Free what we have (if any) and allocate some new ones.
- */
- if (bufs != NULL)
- free((char *)bufs);
- bufs = (struct buf *) calloc(want_nbufs, sizeof(struct buf));
- nbufs = want_nbufs;
- if (bufs == NULL)
- {
- /*
- * Couldn't get that many.
- * Try for a small default number of buffers.
- */
- char message[80];
- sprintf(message,
- "Cannot allocate %d buffers. Using %d buffers.",
- nbufs, DEF_NBUFS);
- error(message);
- bufs = (struct buf *) calloc(DEF_NBUFS, sizeof(struct buf));
- nbufs = DEF_NBUFS;
- if (bufs == NULL)
- {
- /*
- * Couldn't even get the smaller number of bufs.
- * Something is wrong here, don't continue.
- */
- sprintf(message,
- "Cannot even allocate %d buffers! Quitting.",
- DEF_NBUFS);
- error(message);
- quit();
- /*NOTREACHED*/
- }
- }
- }
-
- /*
- * Initialize the buffers to empty.
- * Set up the circular list.
- */
- for (bp = &bufs[0]; bp < &bufs[nbufs]; bp++)
- {
- bp->next = bp + 1;
- bp->prev = bp - 1;
- bp->block = (long)(-1);
- }
- bufs[0].prev = bufs[nbufs-1].next = END_OF_CHAIN;
- buf_head = &bufs[0];
- buf_tail = &bufs[nbufs-1];
- last_piped_block = -1;
- ch_fsize = NULL_POSITION;
- (void) ch_seek((POSITION)0);
- }
- _SHAR_EOF_
-
- echo position.c
- cat >position.c <<'_SHAR_EOF_'
- /*
- * Routines dealing with the "position" table.
- * This is a table which tells the position (in the input file) of the
- * first char on each currently displayed line.
- *
- * {{ The position table is scrolled by moving all the entries.
- * Would be better to have a circular table
- * and just change a couple of pointers. }}
- */
-
- #include "less.h"
- #include "position.h"
-
- #define NPOS 100 /* {{ sc_height must be less than NPOS }} */
- static POSITION table[NPOS]; /* The position table */
-
- extern int sc_width, sc_height;
-
- /*
- * Return the starting file position of a line displayed on the screen.
- * The line may be specified as a line number relative to the top
- * of the screen, but is usually one of these special cases:
- * the top (first) line on the screen
- * the second line on the screen
- * the bottom line on the screen
- * the line after the bottom line on the screen
- */
- public POSITION
- position(where)
- int where;
- {
- switch (where)
- {
- case BOTTOM:
- where = sc_height - 2;
- break;
- case BOTTOM_PLUS_ONE:
- where = sc_height - 1;
- break;
- }
- return (table[where]);
- }
-
- /*
- * Add a new file position to the bottom of the position table.
- */
- public void
- add_forw_pos(pos)
- POSITION pos;
- {
- register int i;
-
- /*
- * Scroll the position table up.
- */
- for (i = 1; i < sc_height; i++)
- table[i-1] = table[i];
- table[sc_height - 1] = pos;
- }
-
- /*
- * Add a new file position to the top of the position table.
- */
- public void
- add_back_pos(pos)
- POSITION pos;
- {
- register int i;
-
- /*
- * Scroll the position table down.
- */
- for (i = sc_height - 1; i > 0; i--)
- table[i] = table[i-1];
- table[0] = pos;
- }
-
- /*
- * Initialize the position table, done whenever we clear the screen.
- */
- public void
- pos_clear()
- {
- register int i;
-
- for (i = 0; i < sc_height; i++)
- table[i] = NULL_POSITION;
- }
-
- /*
- * See if the byte at a specified position is currently on the screen.
- * Check the position table to see if the position falls within its range.
- * Return the position table entry if found, -1 if not.
- */
- public int
- onscreen(pos)
- POSITION pos;
- {
- register int i;
-
- if (pos < table[0])
- return (-1);
- for (i = 1; i < sc_height; i++)
- if (pos < table[i])
- return (i-1);
- return (-1);
- }
- _SHAR_EOF_
-
- echo input.c
- cat >input.c <<'_SHAR_EOF_'
- /*
- * High level routines dealing with getting lines of input
- * from the file being viewed.
- *
- * When we speak of "lines" here, we mean PRINTABLE lines;
- * lines processed with respect to the screen width.
- * We use the term "raw line" to refer to lines simply
- * delimited by newlines; not processed with respect to screen width.
- */
-
- #include "less.h"
-
- extern int squeeze;
- extern char *line;
-
- /*
- * Get the next line.
- * A "current" position is passed and a "new" position is returned.
- * The current position is the position of the first character of
- * a line. The new position is the position of the first character
- * of the NEXT line. The line obtained is the line starting at curr_pos.
- */
- public POSITION
- forw_line(curr_pos)
- POSITION curr_pos;
- {
- POSITION new_pos;
- register int c;
-
- if (curr_pos == NULL_POSITION || ch_seek(curr_pos))
- return (NULL_POSITION);
-
- c = ch_forw_get();
- if (c == EOF)
- return (NULL_POSITION);
-
- prewind();
- for (;;)
- {
- if (c == '\n' || c == EOF)
- {
- /*
- * End of the line.
- */
- new_pos = ch_tell();
- break;
- }
-
- /*
- * Append the char to the line and get the next char.
- */
- if (pappend(c))
- {
- /*
- * The char won't fit in the line; the line
- * is too long to print in the screen width.
- * End the line here.
- */
- new_pos = ch_tell() - 1;
- break;
- }
- c = ch_forw_get();
- }
- (void) pappend('\0');
-
- if (squeeze && *line == '\0')
- {
- /*
- * This line is blank.
- * Skip down to the last contiguous blank line
- * and pretend it is the one which we are returning.
- */
- while ((c = ch_forw_get()) == '\n')
- ;
- if (c != EOF)
- (void) ch_back_get();
- new_pos = ch_tell();
- }
-
- return (new_pos);
- }
-
- /*
- * Get the previous line.
- * A "current" position is passed and a "new" position is returned.
- * The current position is the position of the first character of
- * a line. The new position is the position of the first character
- * of the PREVIOUS line. The line obtained is the one starting at new_pos.
- */
- public POSITION
- back_line(curr_pos)
- POSITION curr_pos;
- {
- POSITION new_pos, begin_new_pos;
- int c;
-
- if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
- ch_seek(curr_pos-1))
- return (NULL_POSITION);
-
- if (squeeze)
- {
- /*
- * Find out if the "current" line was blank.
- */
- (void) ch_forw_get(); /* Skip the newline */
- c = ch_forw_get(); /* First char of "current" line */
- (void) ch_back_get(); /* Restore our position */
- (void) ch_back_get();
-
- if (c == '\n')
- {
- /*
- * The "current" line was blank.
- * Skip over any preceeding blank lines,
- * since we skipped them in forw_line().
- */
- while ((c = ch_back_get()) == '\n')
- ;
- if (c == EOF)
- return (NULL_POSITION);
- (void) ch_forw_get();
- }
- }
-
- /*
- * Scan backwards until we hit the beginning of the line.
- */
- for (;;)
- {
- c = ch_back_get();
- if (c == '\n')
- {
- /*
- * This is the newline ending the previous line.
- * We have hit the beginning of the line.
- */
- new_pos = ch_tell() + 1;
- break;
- }
- if (c == EOF)
- {
- /*
- * We have hit the beginning of the file.
- * This must be the first line in the file.
- * This must, of course, be the beginning of the line.
- */
- new_pos = ch_tell();
- break;
- }
- }
-
- /*
- * Now scan forwards from the beginning of this line.
- * We keep discarding "printable lines" (based on screen width)
- * until we reach the curr_pos.
- *
- * {{ This algorithm is pretty inefficient if the lines
- * are much longer than the screen width,
- * but I don't know of any better way. }}
- */
- if (ch_seek(new_pos))
- return (NULL_POSITION);
- loop:
- begin_new_pos = new_pos;
- prewind();
-
- do
- {
- c = ch_forw_get();
- new_pos++;
- if (c == '\n')
- break;
- if (pappend(c))
- {
- /*
- * Got a full printable line, but we haven't
- * reached our curr_pos yet. Discard the line
- * and start a new one.
- */
- (void) pappend('\0');
- (void) ch_back_get();
- new_pos--;
- goto loop;
- }
- } while (new_pos < curr_pos);
-
- (void) pappend('\0');
-
- return (begin_new_pos);
- }
- _SHAR_EOF_
-
- echo output.c
- cat >output.c <<'_SHAR_EOF_'
- /*
- * High level routines dealing with the output to the screen.
- */
-
- #include "less.h"
-
- public int errmsgs; /* Count of messages displayed by error() */
-
- extern int sigs;
- extern int sc_width, sc_height;
- extern int ul_width, ue_width;
- extern int so_width, se_width;
- extern int bo_width, be_width;
- extern int tabstop;
- extern int twiddle;
- extern int any_display;
- extern char *line;
- extern char *first_cmd;
-
- /*
- * Display the line which is in the line buffer.
- */
- public void
- put_line()
- {
- register char *p;
- register int c;
- register int column;
- extern int auto_wrap, ignaw;
-
- if (sigs)
- /*
- * Don't output if a signal is pending.
- */
- return;
-
- if (line == NULL)
- line = (twiddle) ? "~" : "";
-
- column = 0;
- for (p = line; *p != '\0'; p++)
- {
- switch (c = *p)
- {
- case UL_CHAR:
- ul_enter();
- column += ul_width;
- break;
- case UE_CHAR:
- ul_exit();
- column += ue_width;
- break;
- case BO_CHAR:
- bo_enter();
- column += bo_width;
- break;
- case BE_CHAR:
- bo_exit();
- column += be_width;
- break;
- case '\t':
- do
- {
- putc(' ');
- column++;
- } while ((column % tabstop) != 0);
- break;
- case '\b':
- putbs();
- column--;
- break;
- default:
- if (c & 0200)
- {
- putc('^');
- putc(c & 0177);
- column += 2;
- } else
- {
- putc(c);
- column++;
- }
- }
- }
- if (column < sc_width || !auto_wrap || ignaw)
- putc('\n');
- }
-
- /*
- * Is a given character a "control" character?
- * {{ ASCII DEPENDENT }}
- */
- public int
- control_char(c)
- int c;
- {
- return (c < ' ' || c == '\177');
- }
-
- /*
- * Return the printable character used to identify a control character
- * (printed after a carat; e.g. '\3' => "^C").
- * {{ ASCII DEPENDENT }}
- */
- public int
- carat_char(c)
- int c;
- {
- return ((c == '\177') ? '?' : (c | 0100));
- }
-
-
- static char obuf[1024];
- static char *ob = obuf;
-
- /*
- * Flush buffered output.
- */
- public void
- flush()
- {
- write(1, obuf, ob-obuf);
- ob = obuf;
- }
-
- /*
- * Discard buffered output.
- */
- public void
- dropout()
- {
- ob = obuf;
- }
-
- /*
- * Output a character.
- */
- public void
- putc(c)
- int c;
- {
- if (ob >= &obuf[sizeof(obuf)])
- flush();
- *ob++ = c;
- }
-
- /*
- * Output a string.
- */
- public void
- puts(s)
- register char *s;
- {
- while (*s != '\0')
- putc(*s++);
- }
-
- /*
- * Output a message in the lower left corner of the screen
- * and wait for carriage return.
- */
-
- static char return_to_continue[] = " (press RETURN)";
-
- public void
- error(s)
- char *s;
- {
- register int c;
- static char buf[2];
-
- errmsgs++;
- if (!any_display)
- {
- /*
- * Nothing has been displayed yet.
- * Output this message on error output (file
- * descriptor 2) and don't wait for a keystroke
- * to continue.
- *
- * This has the desirable effect of producing all
- * error messages on error output if standard output
- * is directed to a file. It also does the same if
- * we never produce any real output; for example, if
- * the input file(s) cannot be opened. If we do
- * eventually produce output, code in edit() makes
- * sure these messages can be seen before they are
- * overwritten or scrolled away.
- */
- write(2, s, strlen(s));
- write(2, "\n", 1);
- return;
- }
-
- lower_left();
- clear_eol();
- so_enter();
- puts(s);
- puts(return_to_continue);
- so_exit();
-
- #if ONLY_RETURN
- while ((c = getc()) != '\n' && c != '\r')
- bell();
- #else
- c = getc();
- if (c != '\n' && c != '\r' && c != ' ')
- {
- buf[0] = c;
- first_cmd = buf;
- }
- #endif
-
- if (strlen(s) + sizeof(return_to_continue) +
- so_width + se_width + 1 > sc_width)
- /*
- * Printing the message has probably scrolled the screen.
- * {{ Unless the terminal doesn't have auto margins,
- * in which case we just hammered on the right margin. }}
- */
- repaint();
- }
-
- #ifdef notdef
- public int
- error_width()
- {
- /*
- * Don't use the last position, because some terminals
- * will scroll if you write in the last char of the last line.
- */
- return (sc_width -
- (sizeof(return_to_continue) + so_width + se_width + 1));
- }
- #endif
- _SHAR_EOF_
-
-
-
-