home *** CD-ROM | disk | FTP | other *** search
Text File | 1987-09-01 | 60.4 KB | 2,915 lines |
- Path: uunet!rs
- From: rs@uunet.UU.NET (Rich Salz)
- Newsgroups: comp.sources.unix
- Subject: v11i033: The 'less' pager, Part01/03
- Message-ID: <1360@uunet.UU.NET>
- Date: 2 Sep 87 17:22:26 GMT
- Organization: UUNET Communications Services, Arlington, VA
- Lines: 2904
- Approved: rs@uunet.UU.NET
-
- Submitted-by: sun!intsc!convgt!mark
- Posting-number: Volume 11, Issue 33
- Archive-name: less3/Part01
-
- [ Less is a pager like more or pg, only with more features. It's also not
- derived from licensed software. --r$ ]
-
- : ---- cut here ---- cut here ---- cut here ---- cut here ---- cut here ----
- : This is a shell archive.
- : Unpack by running /bin/sh.
- echo README
- cat >README <<'_SHAR_EOF_'
- This is the distribution of "less", a paginator similar to "more" or "pg".
- The manual page is in less.nro
-
- INSTALLATION:
-
- 1. Move the distributed source to its own directory and
- unpack it by running "sh" on the distribution file,
- if you have not already done so.
-
- 2. Type "sh ./install" and answer the questions it asks.
- This will generate a makefile.
-
- If you choose not to include some features in your version,
- you may wish to edit the manual page less.nro and/or less.man
- to remove the references to the appropriate commands or options.
-
- (NOTE: there are some pre-generated makefiles for
- various systems, named makefile.sys5, makefile.bsd41,
- etc. which may be used if you wish.)
-
- 3. It is a good idea to look over the generated makefile
- and make sure it looks ok.
-
- 4. Type "make" and watch the fun.
-
- 5. If the make succeeds, it will generate a program "less"
- in your current directory. Test the generated program.
-
- 6. When satisfied that it works, if you wish to install it
- in a public place, type "make install".
-
- If you have any problems building or running "less",
- suggestions, complaints, etc., you may mail to the
- author via USENET at:
- tektronix!nscpdc!convgt!mark
- or ihnp4!nsc!nscpdc!convgt!mark
-
- Note to hackers: comments noting possible improvements are enclosed
- in double curly brackets {{ like this }}.
- _SHAR_EOF_
-
- echo less.help
- cat >less.help <<'_SHAR_EOF_'
- Commands marked with * may be preceeded by a number, N.
-
- H Display this help.
- q Exit.
-
- f, SPACE * Forward N lines, default one screen.
- b * Backward N lines, default one screen.
- e, j, CR * Forward N lines, default 1 line.
- y, k * Backward N lines, default 1 line.
- d * Forward N lines, default 10 or last N to d or u command.
- u * Backward N lines, default 10 or last N to d or u command.
- r Repaint screen.
- R Repaint screen, discarding buffered input.
-
- /pattern * Search forward for N-th line containing the pattern.
- ?pattern * Search backward for N-th line containing the pattern.
- n * Repeat previous search (for N-th occurence).
-
- g * Go to line N, default 1.
- G * Like g, but default is last line in file.
- p, % * Position to N percent into the file.
- m<letter> Mark the current position with <letter>.
- '<letter> Return to a previously marked position.
- '' Return to previous position.
-
- E [file] Examine a new file.
- N * Examine the next file (from the command line).
- P * Examine the previous file (from the command line).
- = Print current file name.
- V Print version number of "less".
-
- -<flag> Toggle a command line flag.
- +cmd Execute the less cmd each time a new file is examined.
-
- !command Passes the command to a shell to be executed.
- v Edit the current file with $EDITOR.
- _SHAR_EOF_
-
- echo funcs.h
- cat >funcs.h <<'_SHAR_EOF_'
- public void edit ();
- public void next_file ();
- public void prev_file ();
- public void quit ();
- public void init_option ();
- public void toggle_option ();
- public void scan_option ();
- public void forward ();
- public void backward ();
- public void repaint ();
- public void jump_forw ();
- public void jump_back ();
- public void jump_percent ();
- public void jump_loc ();
- public void init_mark ();
- public void setmark ();
- public void lastmark ();
- public void gomark ();
- public int get_back_scroll ();
- public void search ();
- public void end_logfile ();
- public int ch_seek ();
- public int ch_end_seek ();
- public int ch_beg_seek ();
- public POSITION ch_length ();
- public POSITION ch_tell ();
- public int ch_forw_get ();
- public int ch_back_get ();
- public void ch_init ();
- public POSITION position ();
- public void add_forw_pos ();
- public void add_back_pos ();
- public void pos_clear ();
- public int onscreen ();
- public POSITION forw_line ();
- public POSITION back_line ();
- public void put_line ();
- public int control_char ();
- public int carat_char ();
- public void flush ();
- public void dropout ();
- public void putchr ();
- public void putstr ();
- public void error ();
- public void raw_mode ();
- public void get_term ();
- public void init ();
- public void deinit ();
- public void home ();
- public void add_line ();
- public void lower_left ();
- public void bell ();
- public void vbell ();
- public void clear ();
- public void clear_eol ();
- public void so_enter ();
- public void so_exit ();
- public void ul_enter ();
- public void ul_exit ();
- public void bo_enter ();
- public void bo_exit ();
- public void backspace ();
- public void putbs ();
- public char * eq_message ();
- public char * pr_string ();
- public void prewind ();
- public int pappend ();
- public POSITION forw_raw_line ();
- public POSITION back_raw_line ();
- public void init_signals ();
- public void psignals ();
- public void lsystem ();
- public char * glob ();
- public char * glob ();
- public char * bad_file ();
- public char * bad_file ();
- public char * errno_message ();
- public char * errno_message ();
- public void help ();
- public void open_getchr ();
- public int getchr ();
- public void commands ();
- _SHAR_EOF_
-
- echo less.h
- cat >less.h <<'_SHAR_EOF_'
- /*
- * Standard include file for "less".
- */
-
- /*
- * Language details.
- */
- #if !VOID
- #define void int
- #endif
- #define public /* PUBLIC FUNCTION */
-
- /*
- * Special types and constants.
- */
- typedef long POSITION;
- /*
- * {{ Warning: if POSITION is changed to other than "long",
- * you may have to change some of the printfs which use "%ld"
- * to print a variable of type POSITION. }}
- */
-
- #define NULL_POSITION ((POSITION)(-1))
-
- #define FILENAME 128 /* Max size of a filename */
-
- #define EOF (0)
- #define NULL (0)
-
- /* How quiet should we be? */
- #define NOT_QUIET 0 /* Ring bell at eof and for errors */
- #define LITTLE_QUIET 1 /* Ring bell only for errors */
- #define VERY_QUIET 2 /* Never ring bell */
-
- /* How should we prompt? */
- #define PR_SHORT 0 /* Prompt with colon */
- #define PR_MEDIUM 1 /* Prompt with message */
- #define PR_LONG 2 /* Prompt with longer message */
-
- /* How should we handle backspaces? */
- #define BS_SPECIAL 0 /* Do special things for underlining and bold */
- #define BS_NORMAL 1 /* \b treated as normal char; actually output */
- #define BS_CONTROL 2 /* \b treated as control char; prints as ^H */
-
- /* Special chars used to tell put_line() to do something special */
- #define UL_CHAR '\201' /* Enter underline mode */
- #define UE_CHAR '\202' /* Exit underline mode */
- #define BO_CHAR '\203' /* Enter boldface mode */
- #define BE_CHAR '\204' /* Exit boldface mode */
-
- #define CONTROL(c) ((c)&037)
- #define SIGNAL(sig,func) signal(sig,func)
-
- /* Library function declarations */
- offset_t lseek();
-
- #include "funcs.h"
- _SHAR_EOF_
-
- echo position.h
- cat >position.h <<'_SHAR_EOF_'
- /*
- * Include file for interfacing to position.c modules.
- */
- #define TOP 0
- #define TOP_PLUS_ONE 1
- #define BOTTOM -1
- #define BOTTOM_PLUS_ONE -2
- _SHAR_EOF_
-
- echo mkfuncs.awk
- cat >mkfuncs.awk <<'_SHAR_EOF_'
- BEGIN { FS="("; state = 0 }
-
- /^ public/ { ftype = $0; state = 1 }
-
- { if (state == 1)
- state = 2
- else if (state == 2)
- { print ftype,$1,"();"; state = 0 }
- }
- _SHAR_EOF_
-
- echo screen.c
- cat >screen.c <<'_SHAR_EOF_'
- /*
- * Routines which deal with the characteristics of the terminal.
- * Uses termcap to be as terminal-independent as possible.
- *
- * {{ Someday this should be rewritten to use curses. }}
- */
-
- #include "less.h"
- #if XENIX
- #include <sys/types.h>
- #include <sys/ioctl.h>
- #endif
-
- #if TERMIO
- #include <termio.h>
- #else
- #include <sgtty.h>
- #endif
-
- #ifdef TIOCGWINSZ
- #include <sys/ioctl.h>
- #else
- /*
- * For the Unix PC (ATT 7300 & 3B1):
- * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
- * whether to include sys/window.h. Use SIGWIND from sys/signal.h instead.
- */
- #include <sys/signal.h>
- #ifdef SIGWIND
- #include <sys/window.h>
- #endif
- #endif
-
- /*
- * Strings passed to tputs() to do various terminal functions.
- */
- static char
- *sc_pad, /* Pad string */
- *sc_home, /* Cursor home */
- *sc_addline, /* Add line, scroll down following lines */
- *sc_lower_left, /* Cursor to last line, first column */
- *sc_move, /* General cursor positioning */
- *sc_clear, /* Clear screen */
- *sc_eol_clear, /* Clear to end of line */
- *sc_s_in, /* Enter standout (highlighted) mode */
- *sc_s_out, /* Exit standout mode */
- *sc_u_in, /* Enter underline mode */
- *sc_u_out, /* Exit underline mode */
- *sc_b_in, /* Enter bold mode */
- *sc_b_out, /* Exit bold mode */
- *sc_visual_bell, /* Visual bell (flash screen) sequence */
- *sc_backspace, /* Backspace cursor */
- *sc_init, /* Startup terminal initialization */
- *sc_deinit; /* Exit terminal de-intialization */
- static int dumb;
- static int hard;
-
- public int auto_wrap; /* Terminal does \r\n when write past margin */
- public int ignaw; /* Terminal ignores \n immediately after wrap */
- public int erase_char, kill_char; /* The user's erase and line-kill chars */
- public int sc_width, sc_height; /* Height & width of screen */
- public int sc_window = -1; /* window size for forward and backward */
- public int bo_width, be_width; /* Printing width of boldface sequences */
- public int ul_width, ue_width; /* Printing width of underline sequences */
- public int so_width, se_width; /* Printing width of standout sequences */
-
- /*
- * These two variables are sometimes defined in,
- * and needed by, the termcap library.
- * It may be necessary on some systems to declare them extern here.
- */
- /*extern*/ short ospeed; /* Terminal output baud rate */
- /*extern*/ char PC; /* Pad character */
-
- extern int quiet; /* If VERY_QUIET, use visual bell for bell */
- extern int know_dumb; /* Don't complain about a dumb terminal */
- extern int back_scroll;
- char *tgetstr();
- char *tgoto();
-
- /*
- * Change terminal to "raw mode", or restore to "normal" mode.
- * "Raw mode" means
- * 1. An outstanding read will complete on receipt of a single keystroke.
- * 2. Input is not echoed.
- * 3. On output, \n is mapped to \r\n.
- * 4. \t is NOT expanded into spaces.
- * 5. Signal-causing characters such as ctrl-C (interrupt),
- * etc. are NOT disabled.
- * It doesn't matter whether an input \n is mapped to \r, or vice versa.
- */
- public void
- raw_mode(on)
- int on;
- {
- #if TERMIO
- struct termio s;
- static struct termio save_term;
-
- if (on)
- {
- /*
- * Get terminal modes.
- */
- ioctl(2, TCGETA, &s);
-
- /*
- * Save modes and set certain variables dependent on modes.
- */
- save_term = s;
- ospeed = s.c_cflag & CBAUD;
- erase_char = s.c_cc[VERASE];
- kill_char = s.c_cc[VKILL];
-
- /*
- * Set the modes to the way we want them.
- */
- s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
- s.c_oflag |= (OPOST|ONLCR|TAB3);
- s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
- s.c_cc[VMIN] = 1;
- s.c_cc[VTIME] = 0;
- } else
- {
- /*
- * Restore saved modes.
- */
- s = save_term;
- }
- ioctl(2, TCSETAW, &s);
- #else
- struct sgttyb s;
- static struct sgttyb save_term;
-
- if (on)
- {
- /*
- * Get terminal modes.
- */
- ioctl(2, TIOCGETP, &s);
-
- /*
- * Save modes and set certain variables dependent on modes.
- */
- save_term = s;
- ospeed = s.sg_ospeed;
- erase_char = s.sg_erase;
- kill_char = s.sg_kill;
-
- /*
- * Set the modes to the way we want them.
- */
- s.sg_flags |= CBREAK;
- s.sg_flags &= ~(ECHO|XTABS);
- } else
- {
- /*
- * Restore saved modes.
- */
- s = save_term;
- }
- ioctl(2, TIOCSETN, &s);
- #endif
- }
-
- static void
- cannot(s)
- char *s;
- {
- char message[100];
-
- if (know_dumb)
- /*
- * He knows he has a dumb terminal, so don't tell him.
- */
- return;
-
- sprintf(message, "WARNING: terminal cannot \"%s\"", s);
- error(message);
- }
-
- /*
- * Get terminal capabilities via termcap.
- */
- public void
- get_term()
- {
- char termbuf[2048];
- char *sp;
- #ifdef TIOCGWINSZ
- struct winsize w;
- #else
- #ifdef WIOCGETD
- struct uwdata w;
- #endif
- #endif
- static char sbuf[1024];
-
- char *getenv();
-
- /*
- * Find out what kind of terminal this is.
- */
- if (tgetent(termbuf, getenv("TERM")) <= 0)
- dumb = 1;
-
- /*
- * Get size of the screen.
- */
- #ifdef TIOCGWINSZ
- if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row)
- sc_height = w.ws_row;
- else
- #else
- #ifdef WIOCGETD
- if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height)
- sc_height = w.uw_height/w.uw_vs;
- else
- #endif
- #endif
- sc_height = tgetnum("li");
- if (dumb || sc_height < 0 || tgetflag("hc"))
- {
- /* Oh no, this is a hardcopy terminal. */
- hard = 1;
- sc_height = 24;
- }
- /*
- * This is terrible - the following if "knows" that it is being
- * executed *after* command line and environment options have
- * already been parsed. Should it be executed in the main program
- * instead?
- */
- if ((sc_window <= 0) || (sc_window >= sc_height))
- sc_window = sc_height-1;
-
- #ifdef TIOCGWINSZ
- if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col)
- sc_width = w.ws_col;
- else
- #ifdef WIOCGETD
- if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width)
- sc_width = w.uw_width/w.uw_hs;
- else
- #endif
- #endif
- sc_width = tgetnum("co");
- if (dumb || sc_width < 0)
- sc_width = 80;
-
- auto_wrap = tgetflag("am");
- ignaw = tgetflag("xn");
-
- /*
- * Assumes termcap variable "sg" is the printing width of
- * the standout sequence, the end standout sequence,
- * the underline sequence, the end underline sequence,
- * the boldface sequence, and the end boldface sequence.
- */
- if ((so_width = tgetnum("sg")) < 0)
- so_width = 0;
- be_width = bo_width = ue_width = ul_width = se_width = so_width;
-
- /*
- * Get various string-valued capabilities.
- */
- sp = sbuf;
-
- sc_pad = (dumb) ? NULL : tgetstr("pc", &sp);
- if (sc_pad != NULL)
- PC = *sc_pad;
-
- sc_init = (dumb) ? NULL : tgetstr("ti", &sp);
- if (sc_init == NULL)
- sc_init = "";
-
- sc_deinit= (dumb) ? NULL : tgetstr("te", &sp);
- if (sc_deinit == NULL)
- sc_deinit = "";
-
- sc_eol_clear = (dumb) ? NULL : tgetstr("ce", &sp);
- if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
- {
- cannot("clear to end of line");
- sc_eol_clear = "";
- }
-
- sc_clear = (dumb) ? NULL : tgetstr("cl", &sp);
- if (hard || sc_clear == NULL || *sc_clear == '\0')
- {
- cannot("clear screen");
- sc_clear = "\n\n";
- }
-
- sc_move = (dumb) ? NULL : tgetstr("cm", &sp);
- if (hard || sc_move == NULL || *sc_move == '\0')
- {
- /*
- * This is not an error here, because we don't
- * always need sc_move.
- * We need it only if we don't have home or lower-left.
- */
- sc_move = "";
- }
-
- sc_s_in = (dumb) ? NULL : tgetstr("so", &sp);
- if (hard || sc_s_in == NULL)
- sc_s_in = "";
-
- sc_s_out = (dumb) ? NULL : tgetstr("se", &sp);
- if (hard || sc_s_out == NULL)
- sc_s_out = "";
-
- sc_u_in = (dumb) ? NULL : tgetstr("us", &sp);
- if (hard || sc_u_in == NULL)
- sc_u_in = sc_s_in;
-
- sc_u_out = (dumb) ? NULL : tgetstr("ue", &sp);
- if (hard || sc_u_out == NULL)
- sc_u_out = sc_s_out;
-
- sc_b_in = (dumb) ? NULL : tgetstr("md", &sp);
- if (hard || sc_b_in == NULL)
- {
- sc_b_in = sc_s_in;
- sc_b_out = sc_s_out;
- } else
- {
- sc_b_out = (dumb) ? NULL : tgetstr("me", &sp);
- if (hard || sc_b_out == NULL)
- sc_b_out = "";
- }
-
- sc_visual_bell = (dumb) ? NULL : tgetstr("vb", &sp);
- if (hard || sc_visual_bell == NULL)
- sc_visual_bell = "";
-
- sc_home = (dumb) ? NULL : tgetstr("ho", &sp);
- if (hard || sc_home == NULL || *sc_home == '\0')
- {
- if (*sc_move == '\0')
- {
- cannot("home cursor");
- /*
- * This last resort for sc_home is supposed to
- * be an up-arrow suggesting moving to the
- * top of the "virtual screen". (The one in
- * your imagination as you try to use this on
- * a hard copy terminal.)
- */
- sc_home = "|\b^";
- } else
- {
- /*
- * No "home" string,
- * but we can use "move(0,0)".
- */
- strcpy(sp, tgoto(sc_move, 0, 0));
- sc_home = sp;
- sp += strlen(sp) + 1;
- }
- }
-
- sc_lower_left = (dumb) ? NULL : tgetstr("ll", &sp);
- if (hard || sc_lower_left == NULL || *sc_lower_left == '\0')
- {
- if (*sc_move == '\0')
- {
- cannot("move cursor to lower left of screen");
- sc_lower_left = "\r";
- } else
- {
- /*
- * No "lower-left" string,
- * but we can use "move(0,last-line)".
- */
- strcpy(sp, tgoto(sc_move, 0, sc_height-1));
- sc_lower_left = sp;
- sp += strlen(sp) + 1;
- }
- }
-
- /*
- * To add a line at top of screen and scroll the display down,
- * we use "al" (add line) or "sr" (scroll reverse).
- */
- if (dumb)
- sc_addline = NULL;
- else if ((sc_addline = tgetstr("al", &sp)) == NULL ||
- *sc_addline == '\0')
- sc_addline = tgetstr("sr", &sp);
-
- if (hard || sc_addline == NULL || *sc_addline == '\0')
- {
- cannot("scroll backwards");
- sc_addline = "";
- /* Force repaint on any backward movement */
- back_scroll = 0;
- }
-
- if (dumb || tgetflag("bs"))
- sc_backspace = "\b";
- else
- {
- sc_backspace = tgetstr("bc", &sp);
- if (sc_backspace == NULL || *sc_backspace == '\0')
- sc_backspace = "\b";
- }
- }
-
-
- /*
- * Below are the functions which perform all the
- * terminal-specific screen manipulation.
- */
-
-
- /*
- * Initialize terminal
- */
- public void
- init()
- {
- tputs(sc_init, sc_height, putchr);
- }
-
- /*
- * Deinitialize terminal
- */
- public void
- deinit()
- {
- tputs(sc_deinit, sc_height, putchr);
- }
-
- /*
- * Home cursor (move to upper left corner of screen).
- */
- public void
- home()
- {
- tputs(sc_home, 1, putchr);
- }
-
- /*
- * Add a blank line (called with cursor at home).
- * Should scroll the display down.
- */
- public void
- add_line()
- {
- tputs(sc_addline, sc_height, putchr);
- }
-
- /*
- * Move cursor to lower left corner of screen.
- */
- public void
- lower_left()
- {
- tputs(sc_lower_left, 1, putchr);
- }
-
- /*
- * Ring the terminal bell.
- */
- public void
- bell()
- {
- if (quiet == VERY_QUIET)
- vbell();
- else
- putchr('\7');
- }
-
- /*
- * Output the "visual bell", if there is one.
- */
- public void
- vbell()
- {
- if (*sc_visual_bell == '\0')
- return;
- tputs(sc_visual_bell, sc_height, putchr);
- }
-
- /*
- * Clear the screen.
- */
- public void
- clear()
- {
- tputs(sc_clear, sc_height, putchr);
- }
-
- /*
- * Clear from the cursor to the end of the cursor's line.
- * {{ This must not move the cursor. }}
- */
- public void
- clear_eol()
- {
- tputs(sc_eol_clear, 1, putchr);
- }
-
- /*
- * Begin "standout" (bold, underline, or whatever).
- */
- public void
- so_enter()
- {
- tputs(sc_s_in, 1, putchr);
- }
-
- /*
- * End "standout".
- */
- public void
- so_exit()
- {
- tputs(sc_s_out, 1, putchr);
- }
-
- /*
- * Begin "underline" (hopefully real underlining,
- * otherwise whatever the terminal provides).
- */
- public void
- ul_enter()
- {
- tputs(sc_u_in, 1, putchr);
- }
-
- /*
- * End "underline".
- */
- public void
- ul_exit()
- {
- tputs(sc_u_out, 1, putchr);
- }
-
- /*
- * Begin "bold"
- */
- public void
- bo_enter()
- {
- tputs(sc_b_in, 1, putchr);
- }
-
- /*
- * End "bold".
- */
- public void
- bo_exit()
- {
- tputs(sc_b_out, 1, putchr);
- }
-
- /*
- * Erase the character to the left of the cursor
- * and move the cursor left.
- */
- public void
- backspace()
- {
- /*
- * Try to erase the previous character by overstriking with a space.
- */
- tputs(sc_backspace, 1, putchr);
- putchr(' ');
- tputs(sc_backspace, 1, putchr);
- }
-
- /*
- * Output a plain backspace, without erasing the previous char.
- */
- public void
- putbs()
- {
- tputs(sc_backspace, 1, putchr);
- }
- _SHAR_EOF_
-
- echo prompt.c
- cat >prompt.c <<'_SHAR_EOF_'
- /*
- * Prompting and other messages.
- * There are three flavors of prompts, SHORT, MEDIUM and LONG,
- * selected by the -m/-M options.
- * A prompt is either a colon or a message composed of various
- * pieces, such as the name of the file being viewed, the percentage
- * into the file, etc.
- */
-
- #include "less.h"
- #include "position.h"
-
- extern int pr_type;
- extern int ispipe;
- extern int hit_eof;
- extern int new_file;
- extern int sc_width;
- extern char current_file[];
- extern int ac;
- extern char **av;
- extern int curr_ac;
-
- /*
- * Prototypes for the three flavors of prompts.
- * These strings are expanded by pr_expand().
- */
- char *prproto[] = {
- "fo", /* PR_SHORT */
- "foP", /* PR_MEDIUM */
- "Fobp" /* PR_LONG */
- };
-
- static char message[200];
- static char *mp;
-
- static void
- setmp()
- {
- mp = message + strlen(message);
- }
-
- /*
- * Append the name of the current file (to the message buffer).
- */
- static void
- ap_filename()
- {
- if (ispipe)
- return;
- strtcpy(mp, current_file, &message[sizeof(message)] - mp);
- setmp();
- }
-
- /*
- * Append the "file N of M" message.
- */
- static void
- ap_of()
- {
- if (ac <= 1)
- return;
- sprintf(mp, " (file %d of %d)", curr_ac+1, ac);
- setmp();
- }
-
- /*
- * Append the byte offset into the current file.
- */
- static void
- ap_byte()
- {
- POSITION pos, len;
-
- pos = position(BOTTOM_PLUS_ONE);
- if (pos == NULL_POSITION)
- pos = ch_length();
- if (pos != NULL_POSITION)
- {
- sprintf(mp, " byte %ld", (long)pos);
- setmp();
- len = ch_length();
- if (len > 0)
- {
- sprintf(mp, "/%ld", (long)len);
- setmp();
- }
- }
- }
-
- /*
- * Append the percentage into the current file.
- * If we cannot find the percentage and must_print is true,
- * use the byte offset.
- */
- static void
- ap_percent(must_print)
- {
- POSITION pos,len;
-
- pos = position(BOTTOM_PLUS_ONE);
- len = ch_length();
- if (len > 0 && pos != NULL_POSITION)
- {
- sprintf(mp, " (%ld%%)", (100 * (long)pos) / len);
- setmp();
- } else if (must_print)
- ap_byte();
- }
-
- /*
- * Append the end-of-file message.
- */
- static void
- ap_eof()
- {
- strcpy(mp, " (END)");
- setmp();
- if (curr_ac + 1 < ac)
- {
- sprintf(mp, " - Next: %s", av[curr_ac+1]);
- setmp();
- }
- }
-
- /*
- * Construct a message based on a prototype string.
- */
- static char *
- pr_expand(proto, maxwidth)
- char *proto;
- int maxwidth;
- {
- register char *p;
-
- mp = message;
-
- for (p = proto; *p != '\0'; p++)
- {
- if (maxwidth > 0 && mp >= message + maxwidth)
- {
- /*
- * Truncate to the screen width.
- * {{ This isn't very nice. }}
- */
- mp = message + maxwidth;
- break;
- }
- switch (*p)
- {
- case 'f':
- if (new_file)
- ap_filename();
- break;
- case 'F':
- ap_filename();
- break;
- case 'o':
- if (new_file)
- ap_of();
- break;
- case 'O':
- ap_of();
- break;
- case 'b':
- ap_byte();
- break;
- case 'p':
- if (!hit_eof)
- ap_percent(0);
- break;
- case 'P':
- if (!hit_eof)
- ap_percent(1);
- break;
- case '<':
- while (*++p != '>')
- {
- if (*p == '\0')
- {
- p--;
- break;
- }
- *mp++ = *p;
- }
- break;
- default:
- *mp++ = *p;
- break;
- }
- }
- if (hit_eof)
- ap_eof();
-
- new_file = 0;
- if (mp == message)
- return (NULL);
- *mp = '\0';
- return (message);
- }
-
- /*
- * Return a message suitable for printing by the "=" command.
- */
- public char *
- eq_message()
- {
- return (pr_expand("FObp", 0));
- }
-
- /*
- * Return a prompt.
- * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
- * If we can't come up with an appropriate prompt, return NULL
- * and the caller will prompt with a colon.
- */
- public char *
- pr_string()
- {
- return (pr_expand(prproto[pr_type], sc_width-2));
- }
- _SHAR_EOF_
-
- echo line.c
- cat >line.c <<'_SHAR_EOF_'
- /*
- * Routines to manipulate the "line buffer".
- * The line buffer holds a line of output as it is being built
- * in preparation for output to the screen.
- * We keep track of the PRINTABLE length of the line as it is being built.
- */
-
- #include "less.h"
-
- static char linebuf[1024]; /* Buffer which holds the current output line */
- static char *curr; /* Pointer into linebuf */
- static int column; /* Printable length, accounting for
- backspaces, etc. */
- /*
- * A ridiculously complex state machine takes care of backspaces
- * when in BS_SPECIAL mode. The complexity arises from the attempt
- * to deal with all cases, especially involving long lines with underlining,
- * boldfacing or whatever. There are still some cases which will break it.
- *
- * There are four states:
- * LN_NORMAL is the normal state (not in underline mode).
- * LN_UNDERLINE means we are in underline mode. We expect to get
- * either a sequence like "_\bX" or "X\b_" to continue
- * underline mode, or anything else to end underline mode.
- * LN_BOLDFACE means we are in boldface mode. We expect to get sequences
- * like "X\bX\b...X\bX" to continue boldface mode, or anything
- * else to end boldface mode.
- * LN_UL_X means we are one character after LN_UNDERLINE
- * (we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
- * LN_UL_XB means we are one character after LN_UL_X
- * (we have gotten the backspace in "_\bX" or "X\b_";
- * we expect one more ordinary character,
- * which will put us back in state LN_UNDERLINE).
- * LN_BO_X means we are one character after LN_BOLDFACE
- * (we have gotten the 'X' in "X\bX").
- * LN_BO_XB means we are one character after LN_BO_X
- * (we have gotten the backspace in "X\bX";
- * we expect one more 'X' which will put us back
- * in LN_BOLDFACE).
- */
- static int ln_state; /* Currently in normal/underline/bold/etc mode? */
- #define LN_NORMAL 0 /* Not in underline, boldface or whatever mode */
- #define LN_UNDERLINE 1 /* In underline, need next char */
- #define LN_UL_X 2 /* In underline, got char, need \b */
- #define LN_UL_XB 3 /* In underline, got char & \b, need one more */
- #define LN_BOLDFACE 4 /* In boldface, need next char */
- #define LN_BO_X 5 /* In boldface, got char, need \b */
- #define LN_BO_XB 6 /* In boldface, got char & \b, need same char */
-
- public char *line; /* Pointer to the current line.
- Usually points to linebuf. */
-
- extern int bs_mode;
- extern int tabstop;
- extern int bo_width, be_width;
- extern int ul_width, ue_width;
- extern int sc_width, sc_height;
-
- /*
- * Rewind the line buffer.
- */
- public void
- prewind()
- {
- line = curr = linebuf;
- ln_state = LN_NORMAL;
- column = 0;
- }
-
- /*
- * Append a character to the line buffer.
- * Expand tabs into spaces, handle underlining, boldfacing, etc.
- * Returns 0 if ok, 1 if couldn't fit in buffer.
- */
-
- #define NEW_COLUMN(newcol) if ((newcol) + ((ln_state)?ue_width:0) > sc_width) \
- return (1); else column = (newcol)
-
- public int
- pappend(c)
- int c;
- {
- if (c == '\0')
- {
- /*
- * Terminate any special modes, if necessary.
- * Append a '\0' to the end of the line.
- */
- switch (ln_state)
- {
- case LN_UL_X:
- curr[0] = curr[-1];
- curr[-1] = UE_CHAR;
- curr++;
- break;
- case LN_BO_X:
- curr[0] = curr[-1];
- curr[-1] = BE_CHAR;
- curr++;
- break;
- case LN_UL_XB:
- case LN_UNDERLINE:
- *curr++ = UE_CHAR;
- break;
- case LN_BO_XB:
- case LN_BOLDFACE:
- *curr++ = BE_CHAR;
- break;
- }
- ln_state = LN_NORMAL;
- *curr = '\0';
- return (0);
- }
-
- if (curr > linebuf + sizeof(linebuf) - 12)
- /*
- * Almost out of room in the line buffer.
- * Don't take any chances.
- * {{ Linebuf is supposed to be big enough that this
- * will never happen, but may need to be made
- * bigger for wide screens or lots of backspaces. }}
- */
- return (1);
-
- if (bs_mode == BS_SPECIAL)
- {
- /*
- * Advance the state machine.
- */
- switch (ln_state)
- {
- case LN_NORMAL:
- if (curr <= linebuf + 1 || curr[-1] != '\b')
- break;
-
- if (c == curr[-2])
- goto enter_boldface;
- if (c == '_' || curr[-2] == '_')
- goto enter_underline;
- curr -= 2;
- break;
-
- enter_boldface:
- /*
- * We have "X\bX" (including the current char).
- * Switch into boldface mode.
- */
- if (column + bo_width + be_width + 1 >= sc_width)
- /*
- * Not enough room left on the screen to
- * enter and exit boldface mode.
- */
- return (1);
-
- if (bo_width > 0 &&
- curr > linebuf + 2 && curr[-3] == ' ')
- {
- /*
- * Special case for magic cookie terminals:
- * if the previous char was a space, replace
- * it with the "enter boldface" sequence.
- */
- curr[-3] = BO_CHAR;
- column += bo_width-1;
- } else
- {
- curr[-1] = curr[-2];
- curr[-2] = BO_CHAR;
- column += bo_width;
- curr++;
- }
- goto ln_bo_xb_case;
-
- enter_underline:
- /*
- * We have either "_\bX" or "X\b_" (including
- * the current char). Switch into underline mode.
- */
- if (column + ul_width + ue_width + 1 >= sc_width)
- /*
- * Not enough room left on the screen to
- * enter and exit underline mode.
- */
- return (1);
-
- if (ul_width > 0 &&
- curr > linebuf + 2 && curr[-3] == ' ')
- {
- /*
- * Special case for magic cookie terminals:
- * if the previous char was a space, replace
- * it with the "enter underline" sequence.
- */
- curr[-3] = UL_CHAR;
- column += ul_width-1;
- } else
- {
- curr[-1] = curr[-2];
- curr[-2] = UL_CHAR;
- column += ul_width;
- curr++;
- }
- goto ln_ul_xb_case;
- /*NOTREACHED*/
- case LN_UL_XB:
- /*
- * Termination of a sequence "_\bX" or "X\b_".
- */
- if (c != '_' && curr[-2] != '_' && c == curr[-2])
- {
- /*
- * We seem to have run on from underlining
- * into boldfacing - this is a nasty fix, but
- * until this whole routine is rewritten as a
- * real DFA, ... well ...
- */
- curr[0] = curr[-2];
- curr[-2] = UE_CHAR;
- curr[-1] = BO_CHAR;
- curr += 2; /* char & non-existent backspace */
- ln_state = LN_BO_XB;
- goto ln_bo_xb_case;
- }
- ln_ul_xb_case:
- if (c == '_')
- c = curr[-2];
- curr -= 2;
- ln_state = LN_UNDERLINE;
- break;
- case LN_BO_XB:
- /*
- * Termination of a sequnce "X\bX".
- */
- if (c != curr[-2] && (c == '_' || curr[-2] == '_'))
- {
- /*
- * We seem to have run on from
- * boldfacing into underlining.
- */
- curr[0] = curr[-2];
- curr[-2] = BE_CHAR;
- curr[-1] = UL_CHAR;
- curr += 2; /* char & non-existent backspace */
- ln_state = LN_UL_XB;
- goto ln_ul_xb_case;
- }
- ln_bo_xb_case:
- curr -= 2;
- ln_state = LN_BOLDFACE;
- break;
- case LN_UNDERLINE:
- if (column + ue_width + bo_width + 1 + be_width >= sc_width)
- /*
- * We have just barely enough room to
- * exit underline mode and handle a possible
- * underline/boldface run on mixup.
- */
- return (1);
- ln_state = LN_UL_X;
- break;
- case LN_BOLDFACE:
- if (c == '\b')
- {
- ln_state = LN_BO_XB;
- break;
- }
- if (column + be_width + ul_width + 1 + ue_width >= sc_width)
- /*
- * We have just barely enough room to
- * exit underline mode and handle a possible
- * underline/boldface run on mixup.
- */
- return (1);
- ln_state = LN_BO_X;
- break;
- case LN_UL_X:
- if (c == '\b')
- ln_state = LN_UL_XB;
- else
- {
- /*
- * Exit underline mode.
- * We have to shuffle the chars a bit
- * to make this work.
- */
- curr[0] = curr[-1];
- curr[-1] = UE_CHAR;
- column += ue_width;
- if (ue_width > 0 && curr[0] == ' ')
- /*
- * Another special case for magic
- * cookie terminals: if the next
- * char is a space, replace it
- * with the "exit underline" sequence.
- */
- column--;
- else
- curr++;
- ln_state = LN_NORMAL;
- }
- break;
- case LN_BO_X:
- if (c == '\b')
- ln_state = LN_BO_XB;
- else
- {
- /*
- * Exit boldface mode.
- * We have to shuffle the chars a bit
- * to make this work.
- */
- curr[0] = curr[-1];
- curr[-1] = BE_CHAR;
- column += be_width;
- if (be_width > 0 && curr[0] == ' ')
- /*
- * Another special case for magic
- * cookie terminals: if the next
- * char is a space, replace it
- * with the "exit boldface" sequence.
- */
- column--;
- else
- curr++;
- ln_state = LN_NORMAL;
- }
- break;
- }
- }
-
- if (c == '\t')
- {
- /*
- * Expand a tab into spaces.
- */
- do
- {
- NEW_COLUMN(column+1);
- } while ((column % tabstop) != 0);
- *curr++ = '\t';
- return (0);
- }
-
- if (c == '\b')
- {
- if (bs_mode == BS_CONTROL)
- {
- /*
- * Treat backspace as a control char: output "^H".
- */
- NEW_COLUMN(column+2);
- *curr++ = ('H' | 0200);
- } else
- {
- /*
- * Output a real backspace.
- */
- column--;
- *curr++ = '\b';
- }
- return (0);
- }
-
- if (control_char(c))
- {
- /*
- * Put a "^X" into the buffer.
- * The 0200 bit is used to tell put_line() to prefix
- * the char with a ^. We don't actually put the ^
- * in the buffer because we sometimes need to move
- * chars around, and such movement might separate
- * the ^ from its following character.
- * {{ This should be redone so that we can use an
- * 8 bit (e.g. international) character set. }}
- */
- NEW_COLUMN(column+2);
- *curr++ = (carat_char(c) | 0200);
- return (0);
- }
-
- /*
- * Ordinary character. Just put it in the buffer.
- */
- NEW_COLUMN(column+1);
- *curr++ = c;
- return (0);
- }
-
- /*
- * Analogous to forw_line(), but deals with "raw lines":
- * lines which are not split for screen width.
- * {{ This is supposed to be more efficient than forw_line(). }}
- */
- public POSITION
- forw_raw_line(curr_pos)
- POSITION curr_pos;
- {
- register char *p;
- register int c;
- POSITION new_pos;
-
- if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
- (c = ch_forw_get()) == EOF)
- return (NULL_POSITION);
-
- p = linebuf;
-
- for (;;)
- {
- if (c == '\n' || c == EOF)
- {
- new_pos = ch_tell();
- break;
- }
- if (p >= &linebuf[sizeof(linebuf)-1])
- {
- /*
- * Overflowed the input buffer.
- * Pretend the line ended here.
- * {{ The line buffer is supposed to be big
- * enough that this never happens. }}
- */
- new_pos = ch_tell() - 1;
- break;
- }
- *p++ = c;
- c = ch_forw_get();
- }
- *p = '\0';
- line = linebuf;
- return (new_pos);
- }
-
- /*
- * Analogous to back_line(), but deals with "raw lines".
- * {{ This is supposed to be more efficient than back_line(). }}
- */
- public POSITION
- back_raw_line(curr_pos)
- POSITION curr_pos;
- {
- register char *p;
- register int c;
- POSITION new_pos;
-
- if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
- ch_seek(curr_pos-1))
- return (NULL_POSITION);
-
- p = &linebuf[sizeof(linebuf)];
- *--p = '\0';
-
- for (;;)
- {
- c = ch_back_get();
- if (c == '\n')
- {
- /*
- * This is the newline ending the previous line.
- * We have hit the beginning of the line.
- */
- new_pos = ch_tell() + 1;
- break;
- }
- if (c == 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 = (POSITION)0;
- break;
- }
- if (p <= linebuf)
- {
- /*
- * Overflowed the input buffer.
- * Pretend the line ended here.
- */
- new_pos = ch_tell() + 1;
- break;
- }
- *--p = c;
- }
- line = p;
- return (new_pos);
- }
- _SHAR_EOF_
-
- echo signal.c
- cat >signal.c <<'_SHAR_EOF_'
- /*
- * Routines dealing with signals.
- *
- * A signal usually merely causes a bit to be set in the "signals" word.
- * At some convenient time, the mainline code checks to see if any
- * signals need processing by calling psignal().
- * An exception is made if we are reading from the keyboard when the
- * signal is received. Some operating systems will simply call the
- * signal handler and NOT return from the read (with EINTR).
- * To handle this case, we service the interrupt directly from
- * the handler if we are reading from the keyboard.
- */
-
- #include "less.h"
- #include <signal.h>
- #include <setjmp.h>
-
- /*
- * The type of signal handler functions.
- * Usually int, although it should be void.
- */
- typedef int HANDLER;
-
- /*
- * "sigs" contains bits indicating signals which need to be processed.
- */
- public int sigs;
- #define S_INTERRUPT 01
- #ifdef SIGTSTP
- #define S_STOP 02
- #endif
- #if defined(SIGWINCH) || defined(SIGWIND)
- #define S_WINCH 04
- #endif
-
- extern int reading;
- extern int sc_width, sc_height;
- extern char *first_cmd;
- extern jmp_buf main_loop;
-
- /*
- * Interrupt signal handler.
- */
- static HANDLER
- interrupt()
- {
- SIGNAL(SIGINT, interrupt);
- sigs |= S_INTERRUPT;
- if (reading)
- psignals();
- }
-
- #ifdef SIGTSTP
- /*
- * "Stop" (^Z) signal handler.
- */
- static HANDLER
- stop()
- {
- SIGNAL(SIGTSTP, stop);
- sigs |= S_STOP;
- if (reading)
- psignals();
- }
- #endif
-
- #ifdef SIGWINCH
- /*
- * "Window" change handler
- */
- winch()
- {
- SIGNAL(SIGWINCH, winch);
- sigs |= S_WINCH;
- if (reading)
- psignals();
- }
- #else
- #ifdef SIGWIND
- /*
- * "Window" change handler
- */
- winch()
- {
- SIGNAL(SIGWIND, winch);
- sigs |= S_WINCH;
- if (reading)
- psignals();
- }
- #endif
- #endif
-
- /*
- * Set up the signal handlers.
- */
- public void
- init_signals()
- {
- (void) SIGNAL(SIGINT, interrupt);
- #ifdef SIGTSTP
- (void) SIGNAL(SIGTSTP, stop);
- #endif
- #ifdef SIGWINCH
- (void) SIGNAL(SIGWINCH, winch);
- #else
- #ifdef SIGWIND
- (void) SIGNAL(SIGWIND, winch);
- #endif
- #endif
- }
-
- /*
- * Process any signals we have recieved.
- * A received signal cause a bit to be set in "sigs".
- */
- public void
- psignals()
- {
- register int tsignals;
-
- tsignals = sigs;
- sigs = 0;
- if (tsignals == 0)
- return;
-
- dropout(); /* Discard any buffered output */
-
- #ifdef S_WINCH
- if (tsignals & S_WINCH)
- {
- int old_width, old_height;
- /*
- * Re-execute get_term() to read the new window size.
- */
- old_width = sc_width;
- old_height = sc_height;
- get_term();
- if (sc_width != old_width || sc_height != old_height)
- first_cmd = "r";
- longjmp(main_loop, 1);
- }
- #endif
- #ifdef SIGTSTP
- if (tsignals & S_STOP)
- {
- /*
- * Clean up the terminal.
- */
- #ifdef SIGTTOU
- SIGNAL(SIGTTOU, SIG_IGN);
- #endif
- lower_left();
- clear_eol();
- flush();
- raw_mode(0);
- #ifdef SIGTTOU
- SIGNAL(SIGTTOU, SIG_DFL);
- #endif
- SIGNAL(SIGTSTP, SIG_DFL);
- #if SIGSETMASK
- /*
- * This system will not allow us to send a
- * stop signal (SIGTSTP) to ourself
- * while we are in the signal handler, like maybe now.
- * (This can be the case if we are reading; see comment above.)
- * So we ask the silly system for permission to do so.
- */
- sigsetmask(0);
- #endif
- kill(getpid(), SIGTSTP);
- /*
- * ... Bye bye. ...
- * Hopefully we'll be back later and resume here...
- * Reset the terminal and arrange to repaint the
- * screen when we get back to the main command loop.
- */
- SIGNAL(SIGTSTP, stop);
- raw_mode(1);
- first_cmd = "r";
- longjmp(main_loop, 1);
- }
- #endif
- if (tsignals & S_INTERRUPT)
- {
- bell();
- /*
- * {{ You may wish to replace the bell() with
- * error("Interrupt"); }}
- */
- }
-
- longjmp(main_loop, 1);
- }
- _SHAR_EOF_
-
- echo os.c
- cat >os.c <<'_SHAR_EOF_'
- /*
- * Operating system dependent routines.
- *
- * Most of the stuff in here is based on Unix, but an attempt
- * has been made to make things work on other operating systems.
- * This will sometimes result in a loss of functionality, unless
- * someone rewrites code specifically for the new operating system.
- *
- * The makefile provides defines to decide whether various
- * Unix features are present.
- */
-
- #include "less.h"
- #include <signal.h>
-
- char *getenv();
-
- /*
- * Pass the specified command to a shell to be executed.
- * Like plain "system()", but handles resetting terminal modes, etc.
- */
- public void
- lsystem(cmd)
- char *cmd;
- {
- int inp;
- char cmdbuf[256];
- char *shell;
-
- /*
- * Print the command which is to be executed,
- * unless the command starts with a "-".
- */
- if (cmd[0] == '-')
- cmd++;
- else
- {
- lower_left();
- clear_eol();
- putstr("!");
- putstr(cmd);
- putstr("\n");
- }
-
- /*
- * De-initialize the terminal and take out of raw mode.
- */
- deinit();
- flush();
- raw_mode(0);
-
- /*
- * Restore signals to their defaults.
- */
- SIGNAL(SIGINT, SIG_DFL);
- #ifdef SIGTSTP
- SIGNAL(SIGTSTP, SIG_DFL);
- #endif
- /*
- * Force standard input to be the terminal, "/dev/tty",
- * even if less's standard input is coming from a pipe.
- */
- inp = dup(0);
- close(0);
- if (open("/dev/tty", 0) < 0)
- dup(inp);
-
- /*
- * Pass the command to the system to be executed.
- */
- if ((shell = getenv("SHELL")) != NULL)
- {
- sprintf(cmdbuf, "%s -c \"%s\"", shell, cmd);
- cmd = cmdbuf;
- }
- system(cmd);
-
- /*
- * Restore standard input, reset signals, raw mode, etc.
- */
- close(0);
- dup(inp);
- close(inp);
-
- init_signals();
- raw_mode(1);
- init();
- }
-
- /*
- * Expand a filename, substituting any environment variables, etc.
- * The implementation of this is necessarily very operating system
- * dependent. This implementation is unabashedly only for Unix systems.
- */
- #if GLOB
-
- #include <stdio.h>
- FILE *popen();
-
- public char *
- glob(filename)
- char *filename;
- {
- FILE *f;
- char *p;
- int ch;
- static char ECHO[] = "echo ";
- static char filebuf[FILENAME+sizeof(ECHO)+1];
-
- if (filename[0] == '#')
- return (filename);
- strcpy(filebuf, ECHO);
- strtcpy(filebuf+sizeof(ECHO)-1, filename, sizeof(filebuf)-sizeof(ECHO));
- if ((f = popen(filebuf, "r")) == NULL)
- return (filename);
- for (p = filebuf; p < &filebuf[sizeof(filebuf)-1]; p++)
- {
- if ((ch = getc(f)) == '\n' || ch == EOF)
- break;
- *p = ch;
- }
- *p = '\0';
- pclose(f);
- return (filebuf);
- }
-
- #else
-
- public char *
- glob(filename)
- char *filename;
- {
- return (filename);
- }
-
- #endif
-
-
- /*
- * Returns NULL if the file can be opened and
- * is an ordinary file, otherwise an error message
- * (if it cannot be opened or is a directory, etc.)
- */
-
- #if STAT
-
- #include <sys/types.h>
- #include <sys/stat.h>
-
- public char *
- bad_file(filename, message, len)
- char *filename;
- char *message;
- int len;
- {
- struct stat statbuf;
-
- if (stat(filename, &statbuf) < 0)
- return (errno_message(filename, message, len));
-
- if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
- {
- static char is_dir[] = " is a directory";
- strtcpy(message, filename, len-sizeof(is_dir)-1);
- strcat(message, is_dir);
- return (message);
- }
- if ((statbuf.st_mode & S_IFMT) != S_IFREG)
- {
- static char not_reg[] = " is not a regular file";
- strtcpy(message, filename, len-sizeof(not_reg)-1);
- strcat(message, not_reg);
- return (message);
- }
- return (NULL);
- }
-
- #else
-
- public char *
- bad_file(filename, message, len)
- char *filename;
- char *message;
- int len;
- {
- return (NULL);
- }
-
- #endif
-
- /*
- * Return an error message based on the value of "errno".
- */
-
- #if PERROR
-
- public char *
- errno_message(filename, message, len)
- char *filename;
- char *message;
- int len;
- {
- char *p;
- static char msg[16];
-
- extern char *sys_errlist[];
- extern int sys_nerr;
- extern int errno;
-
- if (errno < sys_nerr)
- p = sys_errlist[errno];
- else
- {
- sprintf(msg, "Error %d", errno);
- p = msg;
- }
- strtcpy(message, filename, len-strlen(p)-3);
- strcat(message, ": ");
- strcat(message, p);
- return (message);
- }
-
- #else
-
- public char *
- errno_message(filename, message, len)
- char *filename;
- char *message;
- int len;
- {
- static char msg[] = ": cannot open";
-
- strtcpy(message, filename, len-sizeof(msg)-1);
- strcat(message, msg);
- return (message);
- }
-
- #endif
- _SHAR_EOF_
-
- echo help.c
- cat >help.c <<'_SHAR_EOF_'
- #include "less.h"
-
- /*
- * Display some help.
- * Just invoke another "less" to display the help file.
- *
- * {{ This makes this function very simple, and makes changing the
- * help file very easy, but it may present difficulties on
- * (non-Unix) systems which do not supply the "system()" function. }}
- */
-
- public void
- help()
- {
- char cmd[200];
-
- sprintf(cmd,
- "-less -m '-Pm<HELP -- Press RETURN for more, or q when done >' %s",
- HELPFILE);
- lsystem(cmd);
- error("End of help");
- }
- _SHAR_EOF_
-
- echo ttyin.c
- cat >ttyin.c <<'_SHAR_EOF_'
- /*
- * Routines dealing with getting input from the keyboard (i.e. from the user).
- */
-
- #include "less.h"
-
- /*
- * The boolean "reading" is set true or false according to whether
- * we are currently reading from the keyboard.
- * This information is used by the signal handling stuff in signal.c.
- * {{ There are probably some race conditions here
- * involving the variable "reading". }}
- */
- public int reading;
-
- static int tty;
-
- /*
- * Open keyboard for input.
- * (Just use file descriptor 2.)
- */
- public void
- open_getchr()
- {
- tty = 2;
- }
-
- /*
- * Get a character from the keyboard.
- */
- public int
- getchr()
- {
- char c;
- int result;
-
- reading = 1;
- do
- {
- flush();
- result = read(tty, &c, 1);
- } while (result != 1);
- reading = 0;
- return (c & 0177);
- }
- _SHAR_EOF_
-
- echo command.c
- cat >command.c <<'_SHAR_EOF_'
- /*
- * User-level command processor.
- */
-
- #include "less.h"
- #include "position.h"
- #include <setjmp.h>
-
- extern jmp_buf main_loop;
- extern int erase_char, kill_char;
- extern int pr_type;
- extern int sigs;
- extern int ispipe;
- extern int quit_at_eof;
- extern int hit_eof;
- extern int sc_width, sc_height;
- extern int sc_window;
- extern char *first_cmd;
- extern char *every_first_cmd;
- extern char version[];
- extern char current_file[];
- extern char *editor;
-
- static char cmdbuf[90]; /* Buffer for holding a multi-char command */
- #if SHELL_ESCAPE
- static char shellcmd[200]; /* For holding last shell command for "!!" */
- #endif
- static char *cp; /* Pointer into cmdbuf */
- static int cmd_col; /* Current column of the multi-char command */
- static char mcc; /* The multi-char command letter (e.g. '/') */
- static char last_mcc; /* The previous mcc */
- static int screen_trashed; /* The screen has been overwritten */
-
- /*
- * Reset command buffer (to empty).
- */
- cmd_reset()
- {
- cp = cmdbuf;
- }
-
- /*
- * Backspace in command buffer.
- */
- static int
- cmd_erase()
- {
- if (cp == cmdbuf)
- /*
- * Backspace past beginning of the string:
- * this usually means abort the command.
- */
- return (1);
-
- if (control_char(*--cp))
- {
- /*
- * Erase an extra character, for the carat.
- */
- backspace();
- cmd_col--;
- }
- backspace();
- cmd_col--;
- return (0);
- }
-
- /*
- * Set up the display to start a new multi-character command.
- */
- start_mcc(c)
- int c;
- {
- mcc = c;
- lower_left();
- clear_eol();
- putchr(mcc);
- cmd_col = 1;
- }
-
- /*
- * Process a single character of a multi-character command, such as
- * a number, or the pattern of a search command.
- */
- static int
- cmd_char(c)
- int c;
- {
- if (c == erase_char)
- {
- if (cmd_erase())
- return (1);
- } else if (c == kill_char)
- {
- /* {{ Could do this faster, but who cares? }} */
- while (cmd_erase() == 0)
- ;
- } else
- {
- /*
- * Append the character to the string,
- * if there is room in the buffer and on the screen.
- */
- if (cp < &cmdbuf[sizeof(cmdbuf)-1] && cmd_col < sc_width-3)
- {
- *cp++ = c;
- if (control_char(c))
- {
- putchr('^');
- cmd_col++;
- c = carat_char(c);
- }
- putchr(c);
- cmd_col++;
- } else
- bell();
- }
- return (0);
- }
-
- /*
- * Return the number currently in the command buffer.
- */
- static int
- cmd_int()
- {
- *cp = '\0';
- cp = cmdbuf;
- return (atoi(cmdbuf));
- }
-
- /*
- * Move the cursor to lower left before executing a command.
- * This looks nicer if the command takes a long time before
- * updating the screen.
- */
- static void
- cmd_exec()
- {
- lower_left();
- flush();
- }
-
- /*
- * Display the appropriate prompt.
- */
- static void
- prompt()
- {
- register char *p;
-
- if (first_cmd != NULL && *first_cmd != '\0')
- /*
- * No prompt necessary if commands are from first_cmd
- * rather than from the user.
- */
- return;
-
- /*
- * If nothing is displayed yet, display starting from line 1.
- */
- if (position(TOP) == NULL_POSITION)
- jump_back(1);
- else if (screen_trashed)
- repaint();
- screen_trashed = 0;
-
- /*
- * Select the proper prompt and display it.
- */
- lower_left();
- clear_eol();
- p = pr_string();
- if (p == NULL)
- putchr(':');
- else
- {
- so_enter();
- putstr(p);
- so_exit();
- }
- }
-
- /*
- * Get command character.
- * The character normally comes from the keyboard,
- * but may come from the "first_cmd" string.
- */
- static int
- getcc()
- {
- if (first_cmd == NULL)
- return (getchr());
-
- if (*first_cmd == '\0')
- {
- /*
- * Reached end of first_cmd input.
- */
- first_cmd = NULL;
- if (cp > cmdbuf && position(TOP) == NULL_POSITION)
- {
- /*
- * Command is incomplete, so try to complete it.
- * There are only two cases:
- * 1. We have "/string" but no newline. Add the \n.
- * 2. We have a number but no command. Treat as #g.
- * (This is all pretty hokey.)
- */
- if (mcc != ':')
- /* Not a number; must be search string */
- return ('\n');
- else
- /* A number; append a 'g' */
- return ('g');
- }
- return (getchr());
- }
- return (*first_cmd++);
- }
-
- /*
- * Main command processor.
- * Accept and execute commands until a quit command, then return.
- */
- public void
- commands()
- {
- register int c;
- register int n;
- register int scroll = 10;
-
- last_mcc = 0;
- setjmp(main_loop);
- mcc = 0;
-
- for (;;)
- {
- /*
- * Display prompt and accept a character.
- */
- psignals(); /* See if any signals need processing */
-
- if (quit_at_eof && (quit_at_eof + hit_eof) > 2)
- /*
- * After hitting end-of-file for the second time,
- * automatically advance to the next file.
- * If there are no more files, quit.
- */
- next_file(1);
-
- cmd_reset();
- prompt();
- c = getcc();
-
- again:
- if (sigs)
- continue;
-
- if (mcc)
- {
- /*
- * We are in a multi-character command.
- * All chars until newline go into the command buffer.
- * (Note that mcc == ':' is a special case that
- * means a number is being entered.)
- */
- if (mcc != ':' && (c == '\n' || c == '\r'))
- {
- char *p;
- static char fcbuf[100];
-
- /*
- * Execute the command.
- */
- *cp = '\0';
- cmd_exec();
- switch (mcc)
- {
- case '/': case '?':
- search(mcc, cmdbuf, n);
- break;
- case '+':
- for (p = cmdbuf; *p == '+' || *p == ' '; p++) ;
- if (*p == '\0')
- every_first_cmd = NULL;
- else
- {
- strtcpy(fcbuf, p, sizeof(fcbuf));
- every_first_cmd = fcbuf;
- }
- break;
- case '-':
- toggle_option(cmdbuf);
- break;
- case 'E':
- /*
- * Ignore leading spaces
- * in the filename.
- */
- for (p = cmdbuf; *p == ' '; p++) ;
- edit(glob(p));
- break;
- #if SHELL_ESCAPE
- case '!':
- /*
- * !! just uses whatever is in shellcmd.
- * Otherwise, copy cmdbuf to shellcmd,
- * replacing any '%' with the current
- * file name.
- */
- if (*cmdbuf != '!')
- {
- register char *fr, *to;
- to = shellcmd;
- for (fr = cmdbuf;
- *fr != '\0'; fr++)
- {
- if (*fr != '%')
- *to++ = *fr;
- else
- {
- strcpy(to,
- current_file);
- to += strlen(to);
- }
- }
- *to = '\0';
- }
- lsystem(shellcmd);
- screen_trashed = 1;
- error("!done");
- break;
- #endif
- }
- mcc = 0;
- } else
- {
- if (mcc == ':' && (c < '0' || c > '9') &&
- c != erase_char && c != kill_char)
- {
- /*
- * This is not part of the number
- * we were entering. Process
- * it as a regular character.
- */
- mcc = 0;
- goto again;
- }
-
- /*
- * Append the char to the command buffer.
- */
- if (cmd_char(c))
- {
- /* Abort the multi-char command. */
- mcc = 0;
- continue;
- }
- c = getcc();
- goto again;
- }
- } else switch (c)
- {
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- /*
- * First digit of a number.
- */
- start_mcc(':');
- goto again;
-
- case 'f':
- case ' ':
- case CONTROL('F'):
- /*
- * Forward one screen.
- */
- n = cmd_int();
- if (n <= 0)
- n = sc_window;
- cmd_exec();
- forward(n, 1);
- break;
-
- case 'b':
- case CONTROL('B'):
- /*
- * Backward one screen.
- */
- n = cmd_int();
- if (n <= 0)
- n = sc_window;
- cmd_exec();
- backward(n, 1);
- break;
-
- case 'e':
- case 'j':
- case '\r':
- case '\n':
- case CONTROL('E'):
- /*
- * Forward N (default 1) line.
- */
- n = cmd_int();
- if (n <= 0)
- n = 1;
- cmd_exec();
- forward(n, 0);
- break;
-
- case 'y':
- case 'k':
- case CONTROL('K'):
- case CONTROL('Y'):
- /*
- * Backward N (default 1) line.
- */
- n = cmd_int();
- if (n <= 0)
- n = 1;
- cmd_exec();
- backward(n, 0);
- break;
-
- case 'd':
- case CONTROL('D'):
- /*
- * Forward N lines
- * (default same as last 'd' or 'u' command).
- */
- n = cmd_int();
- if (n > 0)
- scroll = n;
- cmd_exec();
- forward(scroll, 0);
- break;
-
- case 'u':
- case CONTROL('U'):
- /*
- * Forward N lines
- * (default same as last 'd' or 'u' command).
- */
- n = cmd_int();
- if (n > 0)
- scroll = n;
- cmd_exec();
- backward(scroll, 0);
- break;
-
- case 'R':
- /*
- * Flush buffers, then repaint screen.
- * Don't flush the buffers on a pipe!
- */
- if (!ispipe)
- ch_init(0);
- /* Fall thru */
- case 'r':
- case CONTROL('R'):
- case CONTROL('L'):
- /*
- * Repaint screen.
- */
- repaint();
- break;
-
- case 'g':
- /*
- * Go to line N, default beginning of file.
- */
- n = cmd_int();
- if (n <= 0)
- n = 1;
- cmd_exec();
- jump_back(n);
- break;
-
- case 'p':
- case '%':
- /*
- * Go to a specified percentage into the file.
- */
- n = cmd_int();
- if (n < 0)
- n = 0;
- if (n > 100)
- n = 100;
- cmd_exec();
- jump_percent(n);
- break;
-
- case 'G':
- /*
- * Go to line N, default end of file.
- */
- n = cmd_int();
- cmd_exec();
- if (n <= 0)
- jump_forw();
- else
- jump_back(n);
- break;
-
- case '=':
- case CONTROL('G'):
- /*
- * Print file name, etc.
- */
- error(eq_message());
- break;
-
- case 'V':
- /*
- * Print version number, without the "@(#)".
- */
- error(version+4);
- break;
-
- case 'q':
- /*
- * Exit.
- */
- /*setjmp(main_loop);*/
- quit();
-
- case '/':
- case '?':
- /*
- * Search for a pattern.
- * Accept chars of the pattern until \n.
- */
- n = cmd_int();
- if (n <= 0)
- n = 1;
- start_mcc(c);
- last_mcc = c;
- c = getcc();
- goto again;
-
- case 'n':
- /*
- * Repeat previous search.
- */
- n = cmd_int();
- if (n <= 0)
- n = 1;
- start_mcc(last_mcc);
- cmd_exec();
- search(mcc, (char *)NULL, n);
- mcc = 0;
- break;
-
- case 'H':
- /*
- * Help.
- */
- lower_left();
- clear_eol();
- putstr("help");
- cmd_exec();
- help();
- screen_trashed = 1;
- break;
-
- case 'E':
- /*
- * Edit a new file. Get the filename.
- */
- cmd_reset();
- start_mcc('E');
- putstr("xamine: "); /* This looks nicer */
- cmd_col += 8;
- c = getcc();
- goto again;
-
- case '!':
- #if SHELL_ESCAPE
- /*
- * Shell escape.
- */
- cmd_reset();
- start_mcc('!');
- c = getcc();
- goto again;
- #else
- error("Command not available");
- break;
- #endif
-
- case 'v':
- #if EDITOR
- if (ispipe)
- {
- error("Cannot edit standard input");
- break;
- }
- sprintf(cmdbuf, "%s %s", editor, current_file);
- lsystem(cmdbuf);
- ch_init(0);
- screen_trashed = 1;
- break;
- #else
- error("Command not available");
- break;
- #endif
-
- case 'N':
- /*
- * Examine next file.
- */
- n = cmd_int();
- if (n <= 0)
- n = 1;
- next_file(n);
- break;
-
- case 'P':
- /*
- * Examine previous file.
- */
- n = cmd_int();
- if (n <= 0)
- n = 1;
- prev_file(n);
- break;
-
- case '-':
- /*
- * Toggle a flag setting.
- */
- cmd_reset();
- start_mcc('-');
- c = getcc();
- goto again;
-
- case '+':
- cmd_reset();
- start_mcc('+');
- c = getcc();
- goto again;
-
- case 'm':
- /*
- * Set a mark.
- */
- lower_left();
- clear_eol();
- putstr("mark: ");
- c = getcc();
- if (c == erase_char || c == kill_char)
- break;
- setmark(c);
- break;
-
- case '\'':
- /*
- * Go to a mark.
- */
- lower_left();
- clear_eol();
- putstr("goto mark: ");
- c = getcc();
- if (c == erase_char || c == kill_char)
- break;
- gomark(c);
- break;
-
- default:
- bell();
- break;
- }
- }
- }
- _SHAR_EOF_
-
- echo version.c
- cat >version.c <<'_SHAR_EOF_'
- /*
- * less
- * Copyright (c) 1984,1985 Mark Nudelman
- *
- * This program may be freely used and/or modified,
- * with the following provisions:
- * 1. This notice and the above copyright notice must remain intact.
- * 2. Neither this program, nor any modification of it,
- * may be sold for profit without written consent of the author.
- *
- * -----------------------------------------------------------------
- *
- * This program is a paginator similar to "more",
- * but allows you to move both forward and backward in the file.
- * Commands are based on "more" and "vi".
- *
- * ----------------------- CHANGES ---------------------------------
- *
- * Allowed use on standard input 1/29/84 markn
- * Added E, N, P commands 2/1/84 markn
- * Added '=' command, 'stop' signal handling 4/17/84 markn
- * Added line folding 4/20/84 markn
- * v2: Fixed '=' command to use BOTTOM_PLUS_ONE,
- * instead of TOP, added 'p' & 'v' commands 4/27/84 markn
- * v3: Added -m and -t options, '-' command 5/3/84 markn
- * v4: Added LESS environment variable 5/3/84 markn
- * v5: New comments, fixed '-' command slightly 5/3/84 markn
- * v6: Added -Q, visual bell 5/15/84 markn
- * v7: Fixed jump_back(n) bug: n should count real
- * lines, not folded lines. Also allow number
- * on G command. 5/24/84 markn
- * v8: Re-do -q and -Q commands 5/30/84 markn
- * v9: Added "+<cmd>" argument 9/25/84 markn
- * v10: Fixed bug in -b<n> argument processing 10/10/84 markn
- * v11: Made error() ring bell if \n not entered. 10/18/84 markn
- * -----------------------------------------------------------------
- * v12: Reorganized signal handling and made
- * portable to 4.2bsd. 2/13/85 mark
- * v13: Reword error message for '-' command. 2/16/85 mark
- * v14: Added -bf and -bp variants of -b. 2/22/85 mark
- * v15: Miscellaneous changes. 2/25/85 mark
- * v16: Added -u flag for backspace processing. 3/13/85 mark
- * v17: Added j and k commands,
- * changed -t default. 4/13/85 mark
- * v18: Rewrote signal handling code. 4/20/85 mark
- * v19: Got rid of "verbose" eq_message(). 5/2/85 mark
- * Made search() scroll in some cases.
- * v20: Fixed screen.c ioctls for System V. 5/21/85 mark
- * v21: Fixed some first_cmd bugs. 5/23/85 mark
- * v22: Added support for no RECOMP nor REGCMP. 5/24/85 mark
- * v23: Miscellanous changes and prettying up. 5/25/85 mark
- * Posted to USENET.
- * v24: Added ti,te terminal init & de-init 6/3/85 Mike Kersenbrock
- * v25: Added -U flag, standout mode underlining. 6/8/85 mark
- * v26: Added -M flag. 6/9/85 mark
- * Use underline termcap (us) if it exists.
- * v27: Renamed some variables to make unique in 6/15/85 mark
- * 6 chars. Minor fix to -m.
- * v28: Fixed right margin bug. 6/28/85 mark
- * v29: Incorporated M.Rose's changes to signal.c 6/28/85 mark
- * v30: Fixed stupid bug in argument processing. 6/29/85 mark
- * v31: Added -p flag, changed repaint algorithm. 7/15/85 mark
- * Added kludge for magic cookie terminals.
- * v32: Added cat_file if output not a tty. 7/16/85 mark
- * v33: Added -e flag and EDITOR. 7/23/85 mark
- * v34: Added -s flag. 7/26/85 mark
- * v35: Rewrote option handling; added option.c. 7/27/85 mark
- * v36: Fixed -e flag to work if not last file. 7/29/85 mark
- * v37: Added -x flag. 8/10/85 mark
- * v38: Changed prompting; created prompt.c. 8/19/85 mark
- * v39: (Not -p) does not initially clear screen. 8/24/85 mark
- * v40: Added "skipping" indicator in forw(). 8/26/85 mark
- * Posted to USENET.
- * v41: ONLY_RETURN, control char commands, 9/17/85 mark
- * faster search, other minor fixes.
- * v42: Added ++ command line syntax; 9/25/85 mark
- * ch_fsize for pipes.
- * v43: Added -h flag, changed prim.c algorithms. 10/15/85 mark
- * v44: Made END print in all cases of eof; 10/16/85 mark
- * ignore SIGTTOU after receiving SIGTSTP.
- * v45: Never print backspaces unless -u. 10/16/85 mark
- * v46: Backwards scroll in jump_loc. 10/24/85 mark
- * v47: Fixed bug in edit(): *first_cmd==0 10/30/85 mark
- * v48: Use TIOCSETN instead of TIOCSETP. 11/16/85 mark
- * Added marks (m and ' commands).
- * Posted to USENET.
- * -----------------------------------------------------------------
- * v49: Fixed bug: signal didn't clear mcc. 1/9/86 mark
- * v50: Added ' (quote) to gomark. 1/15/86 mark
- * v51: Added + cmd, fixed problem if first_cmd
- * fails, made g cmd sort of "work" on pipes
- * even if bof is no longer buffered. 1/16/86 mark
- * v52: Made short files work better. 1/17/86 mark
- * v53: Added -P option. 1/20/86 mark
- * v54: Changed help to use HELPFILE. 1/20/86 mark
- * v55: Messages work better if not tty output. 1/23/86 mark
- * v56: Added -l option. 1/24/86 mark
- * v57: Fixed -l to get confirmation before
- * overwriting an existing file. 1/31/86 mark
- * v58: Added filename globbing. 8/28/86 mark
- * v59: Fixed some bugs with very long filenames. 9/15/86 mark
- * v60: Incorporated changes from Leith (Casey)
- * Leedom for boldface and -z option. 9/26/86 mark
- * v61: Got rid of annoying repaints after ! cmd. 9/26/86 mark
- * Posted to USENET.
- * -----------------------------------------------------------------
- * v62: Added is_directory(); change -z default to
- * -1 instead of 24; cat-and-exit if -e and
- * file is less than a screenful. 12/23/86 mark
- * v63: Fixed bug in cat-and-exit if > 1 file. 1/8/87 mark
- * v64: Changed puts/putstr, putc/putchr,
- * getc/getchr to avoid name conflict with
- * stdio functions. 1/12/87 mark
- * v65: Allowed '-' command to change NUMBER
- * valued options (thanks to Gary Puckering) 1/26/87 mark
- * v66: Fixed bug: prepaint should use force=1. 2/13/87 mark
- * v67: Added !! and % expansion to ! command. 2/24/87 mark
- * v68: Added SIGWINCH and TIOCGWINSZ support;
- * changed is_directory to bad_file.
- * (thanks to J. Robert Ward) 2/25/87 mark
- * v69: Added SIGWIND and WIOCGETD (for Unix PC). 2/25/87 mark
- * v70: Changed help cmd from 'h' to 'H'; better
- * error msgs in bad_file, errno_message. 3/13/87 mark
- * v71: Changed -p to -c, made triple -c/-C
- * for clear-eol like more's -c. 5/11/87 mark
- * v72: Added -E, -L, use $SHELL in lsystem(). 6/26/87 mark
- * (thanks to Steve Spearman)
- * v73: Allow Examine "#" for previous file. 6/26/87 mark
- * Posted to USENET 8/25/87.
- * -----------------------------------------------------------------
- */
-
- char version[] = "@(#) less version 73";
- _SHAR_EOF_
-
-
- --
-
- Rich $alz
- Cronus Project, BBN Labs rsalz@bbn.com
- Moderator, comp.sources.unix sources@uunet.uu.net
-