Subject: 'less' for 2.11BSD (#101) Index: local/less 2.11BSD Description: A new program being installed in the source tree. Thanks to Paul Taylor (taylor@oswego.oswego.edu) for doing the port. Repeat-By: N/A. Fix: Cut at the indicated line, unpack the shar file, cd /usr/src/local/less and "make all; make install". ================================cut here===================================== #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # /usr/src/local/less # This archive created: Mon Jan 25 09:27:56 1993 export PATH; PATH=/bin:/usr/bin:$PATH if test ! -d '/usr/src/local/less' then mkdir '/usr/src/local/less' fi cd '/usr/src/local/less' if test -f 'README' then echo shar: "will not over-write existing file 'README'" else sed 's/^X//' << \SHAR_EOF > 'README' XThis is the distribution of "less", a paginator similar to "more" or "pg". XThe manual page is in less.man (nroff source in less.nro). X XINSTALLATION: X X1. Move the distributed source to its own directory and X unpack it by running "sh" on the distribution file, X if you have not already done so. X X2. Type "install" and answer the questions it asks. X This will generate a makefile. X X If you choose not to include some features in your version, X you may wish to edit the manual page less.nro and/or less.man X to remove the references to the appropriate commands or options. X X (NOTE: there are some pre-generated makefiles for X various systems, named makefile.sys5, makefile.bsd41, X etc. which may be used if you wish.) X X3. It is a good idea to look over the generated makefile X and make sure it looks ok. X X4. Type "make" and watch the fun. X X5. If the make succeeds, it will generate a program "less" X in your current directory. Test the generated program. X X6. When satisfied that it works, if you wish to install it X in a public place, type "make install". X XIf you have any problems building or running "less", Xsuggestions, complaints, etc., you may mail to the Xauthor via USENET at: X tektronix!nscpdc!convgt!mark X or ihnp4!nsc!nscpdc!convgt!mark X XNote to hackers: comments noting possible improvements are enclosed Xin double curly brackets {{ like this }}. SHAR_EOF fi if test -f 'ch.c' then echo shar: "will not over-write existing file 'ch.c'" else sed 's/^X//' << \SHAR_EOF > 'ch.c' X/* X * Low level character input from the input file. X * We use these special purpose routines which optimize moving X * both forward and backward from the current read pointer. X */ X X#include "less.h" X Xpublic int file = -1; /* File descriptor of the input file */ X X/* X * Pool of buffers holding the most recently used blocks of the input file. X */ X#define BUFSIZ 1024 Xstruct buf { X struct buf *next, *prev; X long block; X char data[BUFSIZ]; X}; Xstatic struct buf *bufs = NULL; Xpublic int nbufs; X X/* X * The buffer pool is kept as a doubly-linked circular list, X * in order from most- to least-recently used. X * The circular list is anchored by buf_anchor. X */ Xstatic struct { X struct buf *next, *prev; X} buf_anchor; X#define END_OF_CHAIN ((struct buf *)&buf_anchor) X#define buf_head buf_anchor.next X#define buf_tail buf_anchor.prev X X/* X * If we fail to allocate enough memory for buffers, we try to limp X * along with a minimum number of buffers. X */ X#define DEF_NBUFS 2 /* Minimum number of buffers */ X Xextern int clean_data; Xextern int ispipe; Xextern int sigs; X X#if LOGFILE Xextern int logfile; X#endif X X/* X * Current position in file. X * Stored as a block number and an offset into the block. X */ Xstatic long ch_block; Xstatic int ch_offset; X X/* X * Length of file, needed if input is a pipe. X */ Xstatic POSITION ch_fsize; X X/* X * Largest block number read if input is standard input (a pipe). X */ Xstatic long last_piped_block; X X/* X * Get the character pointed to by the read pointer. X * ch_get() is a macro which is more efficient to call X * than fch_get (the function), in the usual case X * that the block desired is at the head of the chain. X */ X#define ch_get() ((buf_head->block == ch_block) ? \ X buf_head->data[ch_offset] : fch_get()) X static int Xfch_get() X{ X register struct buf *bp; X register int n; X register int end; X POSITION pos; X X /* X * Look for a buffer holding the desired block. X */ X for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) X if (bp->block == ch_block) X goto found; X /* X * Block is not in a buffer. X * Take the least recently used buffer X * and read the desired block into it. X */ X bp = buf_tail; X bp->block = ch_block; X pos = ch_block * BUFSIZ; X if (ispipe) X { X /* X * The block requested should be one more than X * the last block read. X */ X if (ch_block != ++last_piped_block) X { X /* This "should not happen". */ X char message[80]; X sprintf(message, "Pipe error: last %ld, want %ld\n", X (long)last_piped_block-1, (long)ch_block); X error(message); X quit(); X } X } else X lseek(file, pos, 0); X X /* X * Read the block. This may take several reads if the input X * is coming from standard input, due to the nature of pipes. X */ X end = 0; X while ((n = read(file, &bp->data[end], BUFSIZ-end)) > 0) X if ((end += n) >= BUFSIZ) X break; X X if (n < 0) X { X error("read error"); X quit(); X } X X#if LOGFILE X /* X * If we have a log file, write this block to it. X */ X if (logfile >= 0 && end > 0) X write(logfile, bp->data, end); X#endif X X /* X * Set an EOF marker in the buffered data itself. X * Then ensure the data is "clean": there are no X * extra EOF chars in the data and that the "meta" X * bit (the 0200 bit) is reset in each char. X */ X if (end < BUFSIZ) X { X ch_fsize = pos + end; X bp->data[end] = EOF; X } X X if (!clean_data) X while (--end >= 0) X { X bp->data[end] &= 0177; X if (bp->data[end] == EOF) X bp->data[end] = '@'; X } X X found: X /* if (buf_head != bp) {this is guaranteed by the ch_get macro} */ X { X /* X * Move the buffer to the head of the buffer chain. X * This orders the buffer chain, most- to least-recently used. X */ X bp->next->prev = bp->prev; X bp->prev->next = bp->next; X X bp->next = buf_head; X bp->prev = END_OF_CHAIN; X buf_head->prev = bp; X buf_head = bp; X } X return (bp->data[ch_offset]); X} X X#if LOGFILE X/* X * Close the logfile. X * If we haven't read all of standard input into it, do that now. X */ X public void Xend_logfile() X{ X static int tried; X X if (logfile < 0) X return; X if (!tried && ch_fsize == NULL_POSITION) X { X tried = 1; X lower_left(); X clear_eol(); X so_enter(); X puts("finishing logfile... (interrupt to abort)"); X so_exit(); X flush(); X while (sigs == 0 && ch_forw_get() != EOF) X ; X } X close(logfile); X logfile = -1; X} X#endif X X/* X * Determine if a specific block is currently in one of the buffers. X */ X static int Xbuffered(block) X long block; X{ X register struct buf *bp; X X for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next) X if (bp->block == block) X return (1); X return (0); X} X X/* X * Seek to a specified position in the file. X * Return 0 if successful, non-zero if can't seek there. X */ X public int Xch_seek(pos) X register POSITION pos; X{ X long new_block; X X new_block = pos / BUFSIZ; X if (!ispipe || new_block == last_piped_block + 1 || buffered(new_block)) X { X /* X * Set read pointer. X */ X ch_block = new_block; X ch_offset = pos % BUFSIZ; X return (0); X } X return (1); X} X X/* X * Seek to the end of the file. X */ X public int Xch_end_seek() X{ X if (ispipe) X { X /* X * Do it the slow way: read till end of data. X */ X while (ch_forw_get() != EOF) X ; X } else X { X (void) ch_seek((POSITION)(lseek(file, (off_t)0, 2))); X } X return (0); X} X X/* X * Seek to the beginning of the file, or as close to it as we can get. X * We may not be able to seek there if input is a pipe and the X * beginning of the pipe is no longer buffered. X */ X public int Xch_beg_seek() X{ X register struct buf *bp, *firstbp; X X /* X * Try a plain ch_seek first. X */ X if (ch_seek((POSITION)0) == 0) X return (0); X X /* X * Can't get to position 0. X * Look thru the buffers for the one closest to position 0. X */ X firstbp = bp = buf_head; X if (bp == END_OF_CHAIN) X return (1); X while ((bp = bp->next) != END_OF_CHAIN) X if (bp->block < firstbp->block) X firstbp = bp; X ch_block = firstbp->block; X ch_offset = 0; X return (0); X} X X/* X * Return the length of the file, if known. X */ X public POSITION Xch_length() X{ X if (ispipe) X return (ch_fsize); X return ((POSITION)(lseek(file, (off_t)0, 2))); X} X X/* X * Return the current position in the file. X */ X public POSITION Xch_tell() X{ X return (ch_block * BUFSIZ + ch_offset); X} X X/* X * Get the current char and post-increment the read pointer. X */ X public int Xch_forw_get() X{ X register int c; X X c = ch_get(); X if (c != EOF && ++ch_offset >= BUFSIZ) X { X ch_offset = 0; X ch_block ++; X } X return (c); X} X X/* X * Pre-decrement the read pointer and get the new current char. X */ X public int Xch_back_get() X{ X register int c; X X if (--ch_offset < 0) X { X if (ch_block <= 0 || (ispipe && !buffered(ch_block-1))) X { X ch_offset = 0; X return (EOF); X } X ch_offset = BUFSIZ - 1; X ch_block--; X } X c = ch_get(); X return (c); X} X X/* X * Initialize the buffer pool to all empty. X * Caller suggests that we use want_nbufs buffers. X */ X public void Xch_init(want_nbufs) X int want_nbufs; X{ X register struct buf *bp; X char *calloc(); X X if (nbufs < want_nbufs) X { X /* X * We don't have enough buffers. X * Free what we have (if any) and allocate some new ones. X */ X if (bufs != NULL) X free((char *)bufs); X bufs = (struct buf *) calloc(want_nbufs, sizeof(struct buf)); X nbufs = want_nbufs; X if (bufs == NULL) X { X /* X * Couldn't get that many. X * Try for a small default number of buffers. X */ X char message[80]; X sprintf(message, X "Cannot allocate %d buffers. Using %d buffers.", X nbufs, DEF_NBUFS); X error(message); X bufs = (struct buf *) calloc(DEF_NBUFS, sizeof(struct buf)); X nbufs = DEF_NBUFS; X if (bufs == NULL) X { X /* X * Couldn't even get the smaller number of bufs. X * Something is wrong here, don't continue. X */ X sprintf(message, X "Cannot even allocate %d buffers! Quitting.", X DEF_NBUFS); X error(message); X quit(); X /*NOTREACHED*/ X } X } X } X X /* X * Initialize the buffers to empty. X * Set up the circular list. X */ X for (bp = &bufs[0]; bp < &bufs[nbufs]; bp++) X { X bp->next = bp + 1; X bp->prev = bp - 1; X bp->block = (long)(-1); X } X bufs[0].prev = bufs[nbufs-1].next = END_OF_CHAIN; X buf_head = &bufs[0]; X buf_tail = &bufs[nbufs-1]; X last_piped_block = -1; X ch_fsize = NULL_POSITION; X (void) ch_seek((POSITION)0); X} SHAR_EOF fi if test -f 'command.c' then echo shar: "will not over-write existing file 'command.c'" else sed 's/^X//' << \SHAR_EOF > 'command.c' X/* X * User-level command processor. X */ X X#include "less.h" X#include "position.h" X#include X Xextern jmp_buf main_loop; Xextern int erase_char, kill_char; Xextern int pr_type; Xextern int sigs; Xextern int ispipe; Xextern int quit_at_eof; Xextern int hit_eof; Xextern int sc_width, sc_height; Xextern int sc_window; Xextern char *first_cmd; Xextern char *every_first_cmd; Xextern char version[]; Xextern char current_file[]; Xextern char *editor; X Xstatic char cmdbuf[90]; /* Buffer for holding a multi-char command */ Xstatic char *cp; /* Pointer into cmdbuf */ Xstatic int cmd_col; /* Current column of the multi-char command */ Xstatic char mcc; /* The multi-char command letter (e.g. '/') */ Xstatic char last_mcc; /* The previous mcc */ Xstatic int screen_trashed; /* The screen has been overwritten */ X X/* X * Reset command buffer (to empty). X */ Xcmd_reset() X{ X cp = cmdbuf; X} X X/* X * Backspace in command buffer. X */ X static int Xcmd_erase() X{ X if (cp == cmdbuf) X /* X * Backspace past beginning of the string: X * this usually means abort the command. X */ X return (1); X X if (control_char(*--cp)) X { X /* X * Erase an extra character, for the carat. X */ X backspace(); X cmd_col--; X } X backspace(); X cmd_col--; X return (0); X} X X/* X * Set up the display to start a new multi-character command. X */ Xstart_mcc(c) X int c; X{ X mcc = c; X lower_left(); X clear_eol(); X putc(mcc); X cmd_col = 1; X} X X/* X * Process a single character of a multi-character command, such as X * a number, or the pattern of a search command. X */ X static int Xcmd_char(c) X int c; X{ X if (c == erase_char) X { X if (cmd_erase()) X return (1); X } else if (c == kill_char) X { X /* {{ Could do this faster, but who cares? }} */ X while (cmd_erase() == 0) X ; X } else X { X /* X * Append the character to the string, X * if there is room in the buffer and on the screen. X */ X if (cp < &cmdbuf[sizeof(cmdbuf)-1] && cmd_col < sc_width-3) X { X *cp++ = c; X if (control_char(c)) X { X putc('^'); X cmd_col++; X c = carat_char(c); X } X putc(c); X cmd_col++; X } else X bell(); X } X return (0); X} X X/* X * Return the number currently in the command buffer. X */ X static int Xcmd_int() X{ X *cp = '\0'; X cp = cmdbuf; X return (atoi(cmdbuf)); X} X X/* X * Move the cursor to lower left before executing a command. X * This looks nicer if the command takes a long time before X * updating the screen. X */ X static void Xcmd_exec() X{ X lower_left(); X flush(); X} X X/* X * Display the appropriate prompt. X */ X static void Xprompt() X{ X register char *p; X X if (first_cmd != NULL && *first_cmd != '\0') X /* X * No prompt necessary if commands are from first_cmd X * rather than from the user. X */ X return; X X /* X * If nothing is displayed yet, display starting from line 1. X */ X if (position(TOP) == NULL_POSITION) X jump_back(1); X else if (screen_trashed) X repaint(); X screen_trashed = 0; X X /* X * Select the proper prompt and display it. X */ X lower_left(); X clear_eol(); X p = pr_string(); X if (p == NULL) X putc(':'); X else X { X so_enter(); X puts(p); X so_exit(); X } X} X X/* X * Get command character. X * The character normally comes from the keyboard, X * but may come from the "first_cmd" string. X */ X static int Xgetcc() X{ X if (first_cmd == NULL) X return (getc()); X X if (*first_cmd == '\0') X { X /* X * Reached end of first_cmd input. X */ X first_cmd = NULL; X if (cp > cmdbuf && position(TOP) == NULL_POSITION) X { X /* X * Command is incomplete, so try to complete it. X * There are only two cases: X * 1. We have "/string" but no newline. Add the \n. X * 2. We have a number but no command. Treat as #g. X * (This is all pretty hokey.) X */ X if (mcc != ':') X /* Not a number; must be search string */ X return ('\n'); X else X /* A number; append a 'g' */ X return ('g'); X } X return (getc()); X } X return (*first_cmd++); X} X X/* X * Main command processor. X * Accept and execute commands until a quit command, then return. X */ X public void Xcommands() X{ X register int c; X register int n; X register int scroll = 10; X X last_mcc = 0; X setjmp(main_loop); X mcc = 0; X X for (;;) X { X /* X * Display prompt and accept a character. X */ X psignals(); /* See if any signals need processing */ X X if (quit_at_eof && hit_eof > 1) X /* X * After hitting end-of-file for the second time, X * automatically advance to the next file. X * If there are no more files, quit. X */ X next_file(1); X X cmd_reset(); X prompt(); X c = getcc(); X X again: X if (sigs) X continue; X X if (mcc) X { X /* X * We are in a multi-character command. X * All chars until newline go into the command buffer. X * (Note that mcc == ':' is a special case that X * means a number is being entered.) X */ X if (mcc != ':' && (c == '\n' || c == '\r')) X { X char *p; X static char fcbuf[100]; X X /* X * Execute the command. X */ X *cp = '\0'; X cmd_exec(); X switch (mcc) X { X case '/': case '?': X search(mcc, cmdbuf, n); X break; X case '+': X for (p = cmdbuf; *p == '+' || *p == ' '; p++) ; X if (*p == '\0') X every_first_cmd = NULL; X else X { X strtcpy(fcbuf, p, sizeof(fcbuf)); X every_first_cmd = fcbuf; X } X break; X case 'E': X /* X * Ignore leading spaces X * in the filename. X */ X for (p = cmdbuf; *p == ' '; p++) ; X edit(glob(p)); X break; X#if SHELL_ESCAPE X case '!': X lsystem(cmdbuf); X screen_trashed = 1; X error("!done"); X break; X#endif X } X mcc = 0; X } else X { X if (mcc == ':' && (c < '0' || c > '9') && X c != erase_char && c != kill_char) X { X /* X * This is not part of the number X * we were entering. Process X * it as a regular character. X */ X mcc = 0; X goto again; X } X X /* X * Append the char to the command buffer. X */ X if (cmd_char(c)) X { X /* Abort the multi-char command. */ X mcc = 0; X continue; X } X c = getcc(); X goto again; X } X } else switch (c) X { X case '0': case '1': case '2': case '3': case '4': X case '5': case '6': case '7': case '8': case '9': X /* X * First digit of a number. X */ X start_mcc(':'); X goto again; X X case 'f': X case ' ': X case CONTROL('F'): X /* X * Forward one screen. X */ X n = cmd_int(); X if (n <= 0) X n = sc_window; X forward(n, 1); X break; X X case 'b': X case CONTROL('B'): X /* X * Backward one screen. X */ X n = cmd_int(); X if (n <= 0) X n = sc_window; X backward(n, 1); X break; X X case 'e': X case 'j': X case '\r': X case '\n': X case CONTROL('E'): X /* X * Forward N (default 1) line. X */ X n = cmd_int(); X if (n <= 0) X n = 1; X forward(n, 0); X break; X X case 'y': X case 'k': X case CONTROL('K'): X case CONTROL('Y'): X /* X * Backward N (default 1) line. X */ X n = cmd_int(); X if (n <= 0) X n = 1; X backward(n, 0); X break; X X case 'd': X case CONTROL('D'): X /* X * Forward N lines X * (default same as last 'd' or 'u' command). X */ X n = cmd_int(); X if (n > 0) X scroll = n; X forward(scroll, 0); X break; X X case 'u': X case CONTROL('U'): X /* X * Forward N lines X * (default same as last 'd' or 'u' command). X */ X n = cmd_int(); X if (n > 0) X scroll = n; X backward(scroll, 0); X break; X X case 'R': X /* X * Flush buffers, then repaint screen. X * Don't flush the buffers on a pipe! X */ X if (!ispipe) X ch_init(0); X /* Fall thru */ X case 'r': X case CONTROL('R'): X case CONTROL('L'): X /* X * Repaint screen. X */ X repaint(); X break; X X case 'g': X /* X * Go to line N, default beginning of file. X */ X n = cmd_int(); X if (n <= 0) X n = 1; X cmd_exec(); X jump_back(n); X break; X X case 'p': X case '%': X /* X * Go to a specified percentage into the file. X */ X n = cmd_int(); X if (n < 0) X n = 0; X if (n > 100) X n = 100; X cmd_exec(); X jump_percent(n); X break; X X case 'G': X /* X * Go to line N, default end of file. X */ X n = cmd_int(); X cmd_exec(); X if (n <= 0) X jump_forw(); X else X jump_back(n); X break; X X case '=': X case CONTROL('G'): X /* X * Print file name, etc. X */ X error(eq_message()); X break; X X case 'V': X /* X * Print version number, without the "@(#)". X */ X error(version+4); X break; X X case 'q': X /* X * Exit. X */ X /*setjmp(main_loop);*/ X quit(); X X case '/': X case '?': X /* X * Search for a pattern. X * Accept chars of the pattern until \n. X */ X n = cmd_int(); X if (n <= 0) X n = 1; X start_mcc(c); X last_mcc = c; X c = getcc(); X goto again; X X case 'n': X /* X * Repeat previous search. X */ X n = cmd_int(); X if (n <= 0) X n = 1; X start_mcc(last_mcc); X cmd_exec(); X search(mcc, (char *)NULL, n); X mcc = 0; X break; X X case 'h': X /* X * Help. X */ X lower_left(); X clear_eol(); X puts("help"); X cmd_exec(); X help(); X screen_trashed = 1; X break; X X case 'E': X /* X * Edit a new file. Get the filename. X */ X cmd_reset(); X start_mcc('E'); X puts("xamine: "); /* This looks nicer */ X cmd_col += 8; X c = getcc(); X goto again; X X case '!': X#if SHELL_ESCAPE X /* X * Shell escape. X */ X cmd_reset(); X start_mcc('!'); X c = getcc(); X goto again; X#else X error("Command not available"); X break; X#endif X X case 'v': X#if EDITOR X if (ispipe) X { X error("Cannot edit standard input"); X break; X } X sprintf(cmdbuf, "%s %s", editor, current_file); X lsystem(cmdbuf); X ch_init(0); X screen_trashed = 1; X break; X#else X error("Command not available"); X break; X#endif X X case 'N': X /* X * Examine next file. X */ X n = cmd_int(); X if (n <= 0) X n = 1; X next_file(n); X break; X X case 'P': X /* X * Examine previous file. X */ X n = cmd_int(); X if (n <= 0) X n = 1; X prev_file(n); X break; X X case '-': X /* X * Toggle a flag setting. X */ X start_mcc('-'); X c = getcc(); X mcc = 0; X if (c == erase_char || c == kill_char) X break; X toggle_option(c); X break; X X case '+': X cmd_reset(); X start_mcc('+'); X c = getcc(); X goto again; X X case 'm': X /* X * Set a mark. X */ X lower_left(); X clear_eol(); X puts("mark: "); X c = getcc(); X if (c == erase_char || c == kill_char) X break; X setmark(c); X break; X X case '\'': X /* X * Go to a mark. X */ X lower_left(); X clear_eol(); X puts("goto mark: "); X c = getcc(); X if (c == erase_char || c == kill_char) X break; X gomark(c); X break; X X default: X bell(); X break; X } X } X} SHAR_EOF fi if test -f 'funcs.h' then echo shar: "will not over-write existing file 'funcs.h'" else sed 's/^X//' << \SHAR_EOF > 'funcs.h' X public void edit (); X public void next_file (); X public void prev_file (); X public void quit (); X public void init_option (); X public void toggle_option (); X public void scan_option (); X public void forward (); X public void backward (); X public void repaint (); X public void jump_forw (); X public void jump_back (); X public void jump_percent (); X public void jump_loc (); X public void init_mark (); X public void setmark (); X public void lastmark (); X public void gomark (); X public int get_back_scroll (); X public void search (); X public void end_logfile (); X public int ch_seek (); X public int ch_end_seek (); X public int ch_beg_seek (); X public POSITION ch_length (); X public POSITION ch_tell (); X public int ch_forw_get (); X public int ch_back_get (); X public void ch_init (); X public POSITION position (); X public void add_forw_pos (); X public void add_back_pos (); X public void pos_clear (); X public int onscreen (); X public POSITION forw_line (); X public POSITION back_line (); X public void put_line (); X public int control_char (); X public int carat_char (); X public void flush (); X public void dropout (); X public void putc (); X public void puts (); X public void error (); X public int error_width (); X public void raw_mode (); X public void get_term (); X public void init (); X public void deinit (); X public void home (); X public void add_line (); X public void lower_left (); X public void bell (); X public void vbell (); X public void clear (); X public void clear_eol (); X public void so_enter (); X public void so_exit (); X public void ul_enter (); X public void ul_exit (); X public void bo_enter (); X public void bo_exit (); X public void backspace (); X public void putbs (); X public char * eq_message (); X public char * pr_string (); X public void prewind (); X public int pappend (); X public POSITION forw_raw_line (); X public POSITION back_raw_line (); X public void init_signals (); X public void psignals (); X public void lsystem (); X public void help (); X public void open_getc (); X public int getc (); X public void commands (); SHAR_EOF fi if test -f 'help.c' then echo shar: "will not over-write existing file 'help.c'" else sed 's/^X//' << \SHAR_EOF > 'help.c' X#include "less.h" X X/* X * Display some help. X * Just invoke another "less" to display the help file. X * X * {{ This makes this function very simple, and makes changing the X * help file very easy, but it may present difficulties on X * (non-Unix) systems which do not supply the "system()" function. }} X */ X X public void Xhelp() X{ X char cmd[200]; X X sprintf(cmd, X "-less -m '-Pm' %s", X HELPFILE); X lsystem(cmd); X error("End of help"); X} SHAR_EOF fi if test -f 'input.c' then echo shar: "will not over-write existing file 'input.c'" else sed 's/^X//' << \SHAR_EOF > 'input.c' X/* X * High level routines dealing with getting lines of input X * from the file being viewed. X * X * When we speak of "lines" here, we mean PRINTABLE lines; X * lines processed with respect to the screen width. X * We use the term "raw line" to refer to lines simply X * delimited by newlines; not processed with respect to screen width. X */ X X#include "less.h" X Xextern int squeeze; Xextern char *line; X X/* X * Get the next line. X * A "current" position is passed and a "new" position is returned. X * The current position is the position of the first character of X * a line. The new position is the position of the first character X * of the NEXT line. The line obtained is the line starting at curr_pos. X */ X public POSITION Xforw_line(curr_pos) X POSITION curr_pos; X{ X POSITION new_pos; X register int c; X X if (curr_pos == NULL_POSITION || ch_seek(curr_pos)) X return (NULL_POSITION); X X c = ch_forw_get(); X if (c == EOF) X return (NULL_POSITION); X X prewind(); X for (;;) X { X if (c == '\n' || c == EOF) X { X /* X * End of the line. X */ X new_pos = ch_tell(); X break; X } X X /* X * Append the char to the line and get the next char. X */ X if (pappend(c)) X { X /* X * The char won't fit in the line; the line X * is too long to print in the screen width. X * End the line here. X */ X new_pos = ch_tell() - 1; X break; X } X c = ch_forw_get(); X } X (void) pappend('\0'); X X if (squeeze && *line == '\0') X { X /* X * This line is blank. X * Skip down to the last contiguous blank line X * and pretend it is the one which we are returning. X */ X while ((c = ch_forw_get()) == '\n') X ; X if (c != EOF) X (void) ch_back_get(); X new_pos = ch_tell(); X } X X return (new_pos); X} X X/* X * Get the previous line. X * A "current" position is passed and a "new" position is returned. X * The current position is the position of the first character of X * a line. The new position is the position of the first character X * of the PREVIOUS line. The line obtained is the one starting at new_pos. X */ X public POSITION Xback_line(curr_pos) X POSITION curr_pos; X{ X POSITION new_pos, begin_new_pos; X int c; X X if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 || X ch_seek(curr_pos-1)) X return (NULL_POSITION); X X if (squeeze) X { X /* X * Find out if the "current" line was blank. X */ X (void) ch_forw_get(); /* Skip the newline */ X c = ch_forw_get(); /* First char of "current" line */ X (void) ch_back_get(); /* Restore our position */ X (void) ch_back_get(); X X if (c == '\n') X { X /* X * The "current" line was blank. X * Skip over any preceeding blank lines, X * since we skipped them in forw_line(). X */ X while ((c = ch_back_get()) == '\n') X ; X if (c == EOF) X return (NULL_POSITION); X (void) ch_forw_get(); X } X } X X /* X * Scan backwards until we hit the beginning of the line. X */ X for (;;) X { X c = ch_back_get(); X if (c == '\n') X { X /* X * This is the newline ending the previous line. X * We have hit the beginning of the line. X */ X new_pos = ch_tell() + 1; X break; X } X if (c == EOF) X { X /* X * We have hit the beginning of the file. X * This must be the first line in the file. X * This must, of course, be the beginning of the line. X */ X new_pos = ch_tell(); X break; X } X } X X /* X * Now scan forwards from the beginning of this line. X * We keep discarding "printable lines" (based on screen width) X * until we reach the curr_pos. X * X * {{ This algorithm is pretty inefficient if the lines X * are much longer than the screen width, X * but I don't know of any better way. }} X */ X if (ch_seek(new_pos)) X return (NULL_POSITION); X loop: X begin_new_pos = new_pos; X prewind(); X X do X { X c = ch_forw_get(); X new_pos++; X if (c == '\n') X break; X if (pappend(c)) X { X /* X * Got a full printable line, but we haven't X * reached our curr_pos yet. Discard the line X * and start a new one. X */ X (void) pappend('\0'); X (void) ch_back_get(); X new_pos--; X goto loop; X } X } while (new_pos < curr_pos); X X (void) pappend('\0'); X X return (begin_new_pos); X} SHAR_EOF fi if test -f 'install' then echo shar: "will not over-write existing file 'install'" else sed 's/^X//' << \SHAR_EOF > 'install' X: X# Installation script for less. X# This script prompts the operator for various information X# and constructs a makefile. X Xecho "This script will build a makefile for less." Xecho "If you already have a file called \"makefile\" it will be overwritten." Xecho "Press RETURN to continue." Xread ans X Xecho "I will ask you some questions about your system." Xecho "If you do not know the answer to any question," Xecho "just press RETURN and I will choose a default for you." Xecho "Press RETURN now." Xread ans X Xecho "Most Unix systems are derived from either System V" Xecho "or Berkeley BSD 4.1, 4.2, 4.3, etc." Xecho "" Xecho "Is your system closest to:" Xecho " 1. System V" Xecho " 2. BSD 2.11 or later" Xecho " 3. BSD 4.1" Xecho " 4. BSD 4.2 or later" Xecho " 5. Xenix" Xecho "Enter a number, or just RETURN if you don't know: \c" Xread ans Xxenix=0 Xcase "X$ans" in XX1) sys=sys5; sysname="System V" ;; XX2) sys=bsd; bsd41=0; bsd2=1; sysname="BSD 2.11" ;; XX3) sys=bsd; bsd41=1; bsd2=0; sysname="BSD 4.1" ;; XX4) sys=bsd; bsd41=0; bsd2=0; sysname="BSD 4.2" ;; XX5) sys=sys5; xenix=1; sysname="Xenix" ;; X*) sys=unknown ;; Xesac Xecho "" X Xcat >makefile <<"EOF" X# Makefile for "less" X# X# Invoked as: X# make all X# or make install X# Plain "make" is equivalent to "make all". X# X# If you add or delete functions, remake funcs.h by doing: X# make newfuncs X# This depends on the coding convention of function headers looking like: X# " \t public \n ( ... ) " X# X# Also provided: X# make lint # Runs "lint" on all the sources. X# make clean # Removes "less" and the .o files. X# make clobber # Pretty much the same as make "clean". X X X########################################################################## X# System-specific parameters X########################################################################## X XEOF X Xcat >>makefile <>makefile <>makefile <>makefile <>makefile <>makefile <>makefile <>makefile <>makefile <>makefile <>makefile <>makefile <>makefile <>makefile <>makefile <<"EOF" X# OPTIM is passed to the compiler and the loader. X# It is normally "-O" but may be, for example, "-g". XOPTIM = -O -i XEOF X Xelse Xcat >>makefile <<"EOF" X# OPTIM is passed to the compiler and the loader. X# It is normally "-O" but may be, for example, "-g". XOPTIM = -O XEOF X Xfi X X Xcat >>makefile <<"EOF" X X X########################################################################## X# Files X########################################################################## X XSRC1 = main.c option.c prim.c ch.c position.c input.c output.c XSRC2 = screen.c prompt.c line.c signal.c help.c ttyin.c command.c version.c XSRC = $(SRC1) $(SRC2) XOBJ = main.o option.o prim.o ch.o position.o input.o output.o screen.o \ X prompt.o line.o signal.o help.o ttyin.o command.o version.o X X X########################################################################## X# Rules X########################################################################## X XDEFS = "-DTERMIO=$(TERMIO)" \ X "-DSIGSETMASK=$(SIGSETMASK)" \ X "-Doff_t=$(off_t)" "-DVOID=$(VOID)" \ X "-DREGCMP=$(REGCMP)" "-DRECOMP=$(RECOMP)" \ X "-DSHELL_ESCAPE=$(SHELL_ESCAPE)" \ X "-DEDITOR=$(EDITOR)" "-DEDIT_PGM=\"$(EDIT_PGM)\"" \ X "-DHELPFILE=\"$(HELPFILE)\"" \ X "-DLOGFILE=$(LOGFILE)" \ X "-DONLY_RETURN=$(ONLY_RETURN)" \ X "-DGLOB=$(GLOB)" \ X "-DXENIX=$(XENIX)" X XCFLAGS = $(OPTIM) $(DEFS) X X Xall: less X Xless: $(OBJ) X cc $(OPTIM) -o less $(OBJ) $(LIBS) X Xinstall: install_man install_less install_help X Xinstall_less: less X for f in $(INSTALL_LESS); do rm -f $$f; cp less $$f; done X touch install_less X Xinstall_help: less.help X for f in $(INSTALL_HELP); do rm -f $$f; cp less.help $$f; done X touch install_help X XEOF X Xif [ "$sys" = "bsd" -a "$bsd2" = "1" ] Xthen Xcat >>makefile <<"EOF" Xinstall_man: $(MANUAL) X rm -f $(INSTALL_MAN) X /usr/man/manroff $(MANUAL) > $(INSTALL_MAN) X chmod 444 $(INSTALL_MAN) X chown bin.bin $(INSTALL_MAN) X touch install_man X XEOF X Xelse Xcat >>makefile <<"EOF" Xinstall_man: $(MANUAL) X for f in $(INSTALL_MAN); do rm -f $$f; cp $(MANUAL) $$f; done X touch install_man X XEOF Xfi X Xcat >>makefile <<"EOF" X$(OBJ): less.h funcs.h X X# help.o depends on makefile for the definition of HELPFILE. Xhelp.o: makefile X Xlint: X lint -hp $(DEFS) $(SRC) X Xnewfuncs: X mv funcs.h funcs.h.OLD X awk -f mkfuncs.awk $(SRC) >funcs.h X XEOF X X Xif [ "$sys" = "bsd" -a "$bsd2" = "1" ] Xthen Xcat >>makefile <<"EOF" Xclean: X rm -f $(OBJ) less install_help install_less install_man core X XEOF X Xelse Xcat >>makefile <<"EOF" Xclean: X rm -f $(OBJ) less X XEOF Xfi X Xcat >>makefile <<"EOF" Xclobber: X rm -f *.o less install_less install_man X Xshar: X shar -v README install less.help makefile.* *.h *.awk > less.shar.a X shar -v less.nro $(SRC1) > less.shar.b X shar -v $(SRC2) > less.shar.c XEOF Xecho "" X Xecho "The makefile has been built." Xecho "You should check it to make sure everything is as you want it to be." Xecho "When you are satisfied with the makefile, just type \"make\"" Xecho "and \"less\" will be built." SHAR_EOF chmod +x 'install' fi if test -f 'less.h' then echo shar: "will not over-write existing file 'less.h'" else sed 's/^X//' << \SHAR_EOF > 'less.h' X/* X * Standard include file for "less". X */ X X/* X * Language details. X */ X#if !VOID X#define void int X#endif X#define public /* PUBLIC FUNCTION */ X X/* X * Special types and constants. X */ Xtypedef long POSITION; X/* X * {{ Warning: if POSITION is changed to other than "long", X * you may have to change some of the printfs which use "%ld" X * to print a variable of type POSITION. }} X */ X X#define NULL_POSITION ((POSITION)(-1)) X X#define EOF (0) X#define NULL (0) X X/* How quiet should we be? */ X#define NOT_QUIET 0 /* Ring bell at eof and for errors */ X#define LITTLE_QUIET 1 /* Ring bell only for errors */ X#define VERY_QUIET 2 /* Never ring bell */ X X/* How should we prompt? */ X#define PR_SHORT 0 /* Prompt with colon */ X#define PR_MEDIUM 1 /* Prompt with message */ X#define PR_LONG 2 /* Prompt with longer message */ X X/* How should we handle backspaces? */ X#define BS_SPECIAL 0 /* Do special things for underlining and bold */ X#define BS_NORMAL 1 /* \b treated as normal char; actually output */ X#define BS_CONTROL 2 /* \b treated as control char; prints as ^H */ X X/* Special chars used to tell put_line() to do something special */ X#define UL_CHAR '\201' /* Enter underline mode */ X#define UE_CHAR '\202' /* Exit underline mode */ X#define BO_CHAR '\203' /* Enter boldface mode */ X#define BE_CHAR '\204' /* Exit boldface mode */ X X#define CONTROL(c) ((c)&037) X#define SIGNAL(sig,func) signal(sig,func) X X/* Library function declarations */ Xoff_t lseek(); X X#include "funcs.h" SHAR_EOF fi if test -f 'less.help' then echo shar: "will not over-write existing file 'less.help'" else sed 's/^X//' << \SHAR_EOF > 'less.help' X X Commands marked with * may be preceeded by a number, N. X X h Display this help. X q Exit. X X f, SPACE * Forward N lines, default one screen. X b * Backward N lines, default one screen. X e, j, CR * Forward N lines, default 1 line. X y, k * Backward N lines, default 1 line. X d * Forward N lines, default 10 or last N to d or u command. X u * Backward N lines, default 10 or last N to d or u command. X r Repaint screen. X R Repaint screen, discarding buffered input. X X /pattern * Search forward for N-th line containing the pattern. X ?pattern * Search backward for N-th line containing the pattern. X n * Repeat previous search (for N-th occurence). X X g * Go to line N, default 1. X G * Like g, but default is last line in file. X p, % * Position to N percent into the file. X m Mark the current position with . X ' Return to a previously marked position. X '' Return to previous position. X X E [file] Examine a new file. X N * Examine the next file (from the command line). X P * Examine the previous file (from the command line). X = Print current file name. X V Print version number of "less". X X - Toggle a command line flag. X +cmd Execute the less cmd each time a new file is examined. X X !command Passes the command to a shell to be executed. X v Edit the current file with $EDITOR. SHAR_EOF fi if test -f 'less.nro' then echo shar: "will not over-write existing file 'less.nro'" else sed 's/^X//' << \SHAR_EOF > 'less.nro' X.TH LESS LOCAL X.SH NAME Xless \- opposite of more X.SH SYNOPSIS X.B "less [-cdepstwmMqQuU] [-h\fIN\fB] [-b[fp]\fIN\fB] [-x\fIN\fB] [-[z]\fIN\fB]" X.br X.B " [-P[mM]\fIstring\fB] [-l\fIlogfile\fB] [+\fIcmd\fB] [\fIfilename\fB]..." X.SH DESCRIPTION X.I Less Xis a program similar to X.I more X(1), but which allows backwards movement Xin the file as well as forward movement. XAlso, X.I less Xdoes not have to read the entire input file before starting, Xso with large input files it starts up faster than text editors like X.I vi X(1). X.I Less Xuses termcap, so it can run on a variety of terminals. XThere is even limited support for hardcopy terminals. X(On a hardcopy terminal, lines which should be printed at the top Xof the screen are prefixed with an up-arrow.) X.PP XCommands are based on both X.I more Xand X.I vi. XCommands may be preceeded by a decimal number, Xcalled N in the descriptions below. XThe number is used by some commands, as indicated. X X.SH COMMANDS XIn the following descriptions, ^X means control-X. X.IP h XHelp: display a summary of these commands. XIf you forget all the other commands, remember this one. X.PP X.IP SPACE XScroll forward N lines, default one window (see option \-z below). XIf N is more than the screen size, only the final screenful is displayed. X.PP X.IP "f or ^F" XSame as SPACE. X.PP X.IP "b or ^B" XScroll backward N lines, default one window (see option \-z below). XIf N is more than the screen size, only the final screenful is displayed. X.PP X.IP RETURN XScroll forward N lines, default 1. XThe entire N lines are displayed, even if N is more than the screen size. X.PP X.IP "e or ^E" XSame as RETURN. X.PP X.IP "j or ^J" XAlso the same as RETURN. X.PP X.IP "y or ^Y" XScroll backward N lines, default 1. XThe entire N lines are displayed, even if N is more than the screen size. X.IP "k or ^K" XSame as y. X.PP X.IP "d or ^D" XScroll forward N lines, default 10. XIf N is specified, it becomes the new default for Xsubsequent d and u commands. X.PP X.IP "u or ^U" XScroll backward N lines, default 10. XIf N is specified, it becomes the new default for Xsubsequent d and u commands. X.PP X.IP "r or ^R or ^L" XRepaint the screen. X.PP X.IP R XRepaint the screen, discarding any buffered input. XUseful if the file is changing while it is being viewed. X.PP X.IP g XGo to line N in the file, default 1 (beginning of file). X(Warning: this may be slow if N is large.) X.PP X.IP G XGo to line N in the file, default the end of the file. X(Warning: this may be slow if standard input, Xrather than a file, is being read.) X.PP X.IP p XGo to a position N percent into the file. XN should be between 0 and 100. X(This is possible if standard input is being read, Xbut only if X.I less Xhas already read to the end of the file. XIt is always fast, but not always useful.) X.PP X.IP % XSame as p. X.PP X.IP m XFollowed by any lowercase letter, Xmarks the current position with that letter. X.PP X.IP "'" X(Single quote.) XFollowed by any lowercase letter, returns to the position which Xwas previously marked with that letter. XFollowed by another single quote, returns to the postion at Xwhich the last "large" movement command was executed. XAll marks are lost when a new file is examined. X.PP X.IP /pattern XSearch forward in the file for the N-th line containing the pattern. XN defaults to 1. XThe pattern is a regular expression, as recognized by X.I ed. XThe search starts at the second line displayed X(but see the \-t option, which changes this). X.PP X.IP ?pattern XSearch backward in the file for the N-th line containing the pattern. XThe search starts at the line immediately before the top line displayed. X.PP X.IP n XRepeat previous search, for N-th line containing the last pattern. X.PP X.IP E [filename] XExamine a new file. XIf the filename is missing, the "current" file (see the N and P commands Xbelow) from the list of files in the command line is re-examined. X.PP X.IP N XExamine the next file (from the list of files given in the command line). XIf a number N is specified (not to be confused with the command N), Xthe N-th next file is examined. X.PP X.IP P XExamine the previous file. XIf a number N is specified, the N-th previous file is examined. X.PP X.IP "= or ^G" XPrints some information about the file being viewed, Xincluding its name Xand the byte offset of the bottom line being displayed. XIf possible, it also prints the length of the file Xand the percent of the file above the last displayed line. X.PP X.IP \- XFollowed by one of the command line option letters (see below), Xthis will toggle the setting of that option Xand print a message describing the new setting. X.PP X.IP +cmd XCauses the specified cmd to be executed each time a new file is examined. XFor example, +G causes X.I less Xto initially display each file starting at the end Xrather than the beginning. X.PP X.IP V XPrints the version number of X.I less Xbeing run. X.PP X.IP q XExits X.I less. X.PP XThe following Xtwo Xcommands may or may not be valid, depending on your particular installation. X.PP X.IP v XInvokes an editor to edit the current file being viewed. XThe editor is taken from the environment variable EDITOR, Xor defaults to "vi". X.PP X.IP "! shell-command" XInvokes a shell to run the shell-command given. X.PP X.SH OPTIONS XCommand line options are described below. XMost options may be changed while X.I less Xis running, via the "\-" command. X.PP XOptions are also taken from the environment variable "LESS". XFor example, if you like Xmore-style prompting, to avoid typing "less \-m ..." each time X.I less Xis invoked, you might tell X.I csh: X.sp Xsetenv LESS m X.sp Xor if you use X.I sh: X.sp XLESS=m; export LESS X.sp XThe environment variable is parsed before the command line, Xso command line options override the LESS environment variable. XA dollar sign ($) may be used to signal the end of an option string. XThis is important only for options like \-P which take a Xfollowing string. X.IP \-s XThe \-s option causes Xconsecutive blank lines to be squeezed into a single blank line. XThis is useful when viewing X.I nroff Xoutput. X.IP \-t XNormally, forward searches start just after Xthe top displayed line (that is, at the second displayed line). XThus forward searches include the currently displayed screen. XThe \-t option causes forward searches to start Xjust after the bottom line displayed, Xthus skipping the currently displayed screen. X.IP \-m XNormally, X.I less Xprompts with a colon. XThe \-m option causes X.I less Xto prompt verbosely (like X.I more), Xwith the percent into the file. X.IP \-M XThe \-M option causes X.I less Xto prompt even more verbosely than X.I more. X.IP \-P XThe \-P option provides a way to tailor the three prompt Xstyles to your own preference. XYou would normally put this option in your LESS environment Xvariable, rather than type it in with each less command. XSuch an option must either be the last option in the LESS variable, Xor be terminated by a dollar sign. X\-P followed by a string changes the default (short) prompt to that string. X\-Pm changes the medium (\-m) prompt to the string, and X\-PM changes the long (\-M) prompt. XThe string consists of a sequence of letters which are replaced Xwith certain predefined strings, as follows: X.br X F file name X.br X f file name, only once X.br X O file n of n X.br X o file n of n, only once X.br X b byte offset X.br X p percent into file X.br X P percent if known, else byte offset X.br XAngle brackets, < and >, may be used to surround a Xliteral string to be included in the prompt. XThe defaults are "fo" for the short prompt, X"foP" for the medium prompt, and X"Fobp" for the long prompt. X.br XExample: Setting your LESS variable to "PmFOP$PMFObp" Xwould change the medium and long prompts to always Xinclude the file name and "file n of n" message. X.br XAnother example: Setting your LESS variable to X.br X"mPm<--Less-->FoPe" Xwould change the medium prompt to the string "--Less--" followed Xby the file name and percent into the file. XIt also selects the medium Xprompt as the default prompt (because of the first "m"). X.IP \-q XNormally, if an attempt is made to scroll past the end of the file Xor before the beginning of the file, the terminal bell is rung to Xindicate this fact. XThe \-q option tells X.I less Xnot to ring the bell at such times. XIf the terminal has a "visual bell", it is used instead. X.IP \-Q XEven if \-q is given, X.I less Xwill ring the bell on certain other errors, Xsuch as typing an invalid character. XThe \-Q option tells X.I less Xto be quiet all the time; that is, never ring the terminal bell. XIf the terminal has a "visual bell", it is used instead. X.IP \-e XNormally the only way to exit less is via the "q" command. XThe \-e option tells less to automatically exit Xthe second time it reaches end-of-file. X.IP \-u XIf the \-u option is given, Xbackspaces are treated as printable characters; Xthat is, they are sent to the terminal when they appear in the input. X.IP \-U XIf the \-U option is given, Xbackspaces are printed as the two character sequence "^H". X.sp XIf neither \-u nor \-U is given, Xbackspaces which appear adjacent to an underscore character Xare treated specially: Xthe underlined text is displayed Xusing the terminal's hardware underlining capability. XAlso, backspaces which appear between two identical characters Xare treated specially: Xthe overstruck text is printed Xusing the terminal's hardware boldface capability. XOther backspaces are deleted, along with the preceeding character. X.IP \-w XNormally, X.I less Xuses a tilde character to represent lines past the end of the file. XThe \-w option causes blank lines to be used instead. X.IP \-d XNormally, X.I less Xwill complain if the terminal is dumb; that is, lacks some important capability, Xsuch as the ability to clear the screen or scroll backwards. XThe \-d option suppresses this complaint X(but does not otherwise change the behavior of the program on a dumb terminal). X.IP \-p XNormally, X.I less Xwill repaint the screen by scrolling from the bottom of the screen. XIf the \-p option is set, when X.I less Xneeds to change the entire display, it will clear the screen Xand paint from the top line down. X.IP \-h XNormally, X.I less Xwill scroll backwards when backwards movement is necessary. XThe \-h option specifies a maximum number of lines to scroll backwards. XIf it is necessary to move backwards more than this many lines, Xthe screen is repainted in a forward direction. X(If the terminal does not have the ability to scroll Xbackwards, \-h0 is implied.) X.IP \-[z] XWhen given a backwards or forwards window command, X.I less Xwill by Xdefault scroll backwards or forwards one screenful of lines. XThe \-z\fIn\fR option changes the default scrolling window size Xto \fIn\fR lines. XIf \fIn\fR is greater than the screen size, Xthe scrolling window size will be set to one screenful. XNote that the "z" is optional for compatibility with X.I more. X.IP -x XThe -x\fIn\fR option sets tab stops every \fIn\fR positions. XThe default for \fIn\fR is 8. X.IP -l XThe -l option, followed immediately by a filename, Xwill cause X.I less Xto copy its input to the named file as it is being viewed. XThis applies only when the input file is a pipe, Xnot an ordinary file. X.IP -b XThe -b\fIn\fR option tells X.I less Xto use a non-standard buffer size. XThere are two standard (default) buffer sizes, Xone is used when a file is being read and the other Xwhen a pipe (standard input) is being read. XThe current defaults are 5 buffers for files and 12 for pipes. X(Buffers are 1024 bytes.) XThe number \fIn\fR specifies a different number of buffers to use. XThe -b may be followed by "f", in which case only Xthe file default is changed, or by "p" in which case only the Xpipe default is changed. Otherwise, both are changed. X.IP -c XNormally, when data is read by X.I less, Xit is scanned to ensure that bit 7 (the high order bit) is turned off in Xeach byte read, and to ensure that there are no null (zero) bytes in Xthe data (null bytes are turned into "@" characters). XIf the data is known to be "clean", Xthe -c option will tell X.I less Xto skip this checking, causing an imperceptible speed improvement. X(However, if the data is not "clean", unpredicatable results may occur.) X.IP + XIf a command line option begins with \fB+\fR, Xthe remainder of that option is taken to be an initial command to X.I less. XFor example, +G tells X.I less Xto start at the end of the file rather than the beginning, Xand +/xyz tells it to start at the first occurence of "xyz" in the file. XAs a special case, + acts like +g; Xthat is, it starts the display at the specified line number X(however, see the caveat under the "g" command above). XIf the option starts with \fB++\fR, the initial command applies to Xevery file being viewed, not just the first one. XThe + command described previously Xmay also be used to set (or change) an initial command for every file. X X.SH BUGS XWhen used on standard input (rather than a file), you can move Xbackwards only a finite amount, corresponding to that portion Xof the file which is still buffered. XThe -b option may be used to expand the buffer space. SHAR_EOF fi if test -f 'line.c' then echo shar: "will not over-write existing file 'line.c'" else sed 's/^X//' << \SHAR_EOF > 'line.c' X/* X * Routines to manipulate the "line buffer". X * The line buffer holds a line of output as it is being built X * in preparation for output to the screen. X * We keep track of the PRINTABLE length of the line as it is being built. X */ X X#include "less.h" X Xstatic char linebuf[1024]; /* Buffer which holds the current output line */ Xstatic char *curr; /* Pointer into linebuf */ Xstatic int column; /* Printable length, accounting for X backspaces, etc. */ X/* X * A ridiculously complex state machine takes care of backspaces X * when in BS_SPECIAL mode. The complexity arises from the attempt X * to deal with all cases, especially involving long lines with underlining, X * boldfacing or whatever. There are still some cases which will break it. X * X * There are four states: X * LN_NORMAL is the normal state (not in underline mode). X * LN_UNDERLINE means we are in underline mode. We expect to get X * either a sequence like "_\bX" or "X\b_" to continue X * underline mode, or anything else to end underline mode. X * LN_BOLDFACE means we are in boldface mode. We expect to get sequences X * like "X\bX\b...X\bX" to continue boldface mode, or anything X * else to end boldface mode. X * LN_UL_X means we are one character after LN_UNDERLINE X * (we have gotten the '_' in "_\bX" or the 'X' in "X\b_"). X * LN_UL_XB means we are one character after LN_UL_X X * (we have gotten the backspace in "_\bX" or "X\b_"; X * we expect one more ordinary character, X * which will put us back in state LN_UNDERLINE). X * LN_BO_X means we are one character after LN_BOLDFACE X * (we have gotten the 'X' in "X\bX"). X * LN_BO_XB means we are one character after LN_BO_X X * (we have gotten the backspace in "X\bX"; X * we expect one more 'X' which will put us back X * in LN_BOLDFACE). X */ Xstatic int ln_state; /* Currently in normal/underline/bold/etc mode? */ X#define LN_NORMAL 0 /* Not in underline, boldface or whatever mode */ X#define LN_UNDERLINE 1 /* In underline, need next char */ X#define LN_UL_X 2 /* In underline, got char, need \b */ X#define LN_UL_XB 3 /* In underline, got char & \b, need one more */ X#define LN_BOLDFACE 4 /* In boldface, need next char */ X#define LN_BO_X 5 /* In boldface, got char, need \b */ X#define LN_BO_XB 6 /* In boldface, got char & \b, need same char */ X Xpublic char *line; /* Pointer to the current line. X Usually points to linebuf. */ X Xextern int bs_mode; Xextern int tabstop; Xextern int bo_width, be_width; Xextern int ul_width, ue_width; Xextern int sc_width, sc_height; X X/* X * Rewind the line buffer. X */ X public void Xprewind() X{ X line = curr = linebuf; X ln_state = LN_NORMAL; X column = 0; X} X X/* X * Append a character to the line buffer. X * Expand tabs into spaces, handle underlining, boldfacing, etc. X * Returns 0 if ok, 1 if couldn't fit in buffer. X */ X X#define NEW_COLUMN(newcol) if ((newcol) + ((ln_state)?ue_width:0) > sc_width) \ X return (1); else column = (newcol) X X public int Xpappend(c) X int c; X{ X if (c == '\0') X { X /* X * Terminate any special modes, if necessary. X * Append a '\0' to the end of the line. X */ X switch (ln_state) X { X case LN_UL_X: X curr[0] = curr[-1]; X curr[-1] = UE_CHAR; X curr++; X break; X case LN_BO_X: X curr[0] = curr[-1]; X curr[-1] = BE_CHAR; X curr++; X break; X case LN_UL_XB: X case LN_UNDERLINE: X *curr++ = UE_CHAR; X break; X case LN_BO_XB: X case LN_BOLDFACE: X *curr++ = BE_CHAR; X break; X } X ln_state = LN_NORMAL; X *curr = '\0'; X return (0); X } X X if (curr > linebuf + sizeof(linebuf) - 12) X /* X * Almost out of room in the line buffer. X * Don't take any chances. X * {{ Linebuf is supposed to be big enough that this X * will never happen, but may need to be made X * bigger for wide screens or lots of backspaces. }} X */ X return (1); X X if (bs_mode == BS_SPECIAL) X { X /* X * Advance the state machine. X */ X switch (ln_state) X { X case LN_NORMAL: X if (curr <= linebuf + 1 || curr[-1] != '\b') X break; X X if (c == curr[-2]) X goto enter_boldface; X if (c == '_' || curr[-2] == '_') X goto enter_underline; X curr -= 2; X break; X Xenter_boldface: X /* X * We have "X\bX" (including the current char). X * Switch into boldface mode. X */ X if (column + bo_width + be_width + 1 >= sc_width) X /* X * Not enough room left on the screen to X * enter and exit boldface mode. X */ X return (1); X X if (bo_width > 0 && X curr > linebuf + 2 && curr[-3] == ' ') X { X /* X * Special case for magic cookie terminals: X * if the previous char was a space, replace X * it with the "enter boldface" sequence. X */ X curr[-3] = BO_CHAR; X column += bo_width-1; X } else X { X curr[-1] = curr[-2]; X curr[-2] = BO_CHAR; X column += bo_width; X curr++; X } X goto ln_bo_xb_case; X Xenter_underline: X /* X * We have either "_\bX" or "X\b_" (including X * the current char). Switch into underline mode. X */ X if (column + ul_width + ue_width + 1 >= sc_width) X /* X * Not enough room left on the screen to X * enter and exit underline mode. X */ X return (1); X X if (ul_width > 0 && X curr > linebuf + 2 && curr[-3] == ' ') X { X /* X * Special case for magic cookie terminals: X * if the previous char was a space, replace X * it with the "enter underline" sequence. X */ X curr[-3] = UL_CHAR; X column += ul_width-1; X } else X { X curr[-1] = curr[-2]; X curr[-2] = UL_CHAR; X column += ul_width; X curr++; X } X goto ln_ul_xb_case; X /*NOTREACHED*/ X case LN_UL_XB: X /* X * Termination of a sequence "_\bX" or "X\b_". X */ X if (c != '_' && curr[-2] != '_' && c == curr[-2]) X { X /* X * We seem to have run on from underlining X * into boldfacing - this is a nasty fix, but X * until this whole routine is rewritten as a X * real DFA, ... well ... X */ X curr[0] = curr[-2]; X curr[-2] = UE_CHAR; X curr[-1] = BO_CHAR; X curr += 2; /* char & non-existent backspace */ X ln_state = LN_BO_XB; X goto ln_bo_xb_case; X } Xln_ul_xb_case: X if (c == '_') X c = curr[-2]; X curr -= 2; X ln_state = LN_UNDERLINE; X break; X case LN_BO_XB: X /* X * Termination of a sequnce "X\bX". X */ X if (c != curr[-2] && (c == '_' || curr[-2] == '_')) X { X /* X * We seem to have run on from X * boldfacing into underlining. X */ X curr[0] = curr[-2]; X curr[-2] = BE_CHAR; X curr[-1] = UL_CHAR; X curr += 2; /* char & non-existent backspace */ X ln_state = LN_UL_XB; X goto ln_ul_xb_case; X } Xln_bo_xb_case: X curr -= 2; X ln_state = LN_BOLDFACE; X break; X case LN_UNDERLINE: X if (column + ue_width + bo_width + 1 + be_width >= sc_width) X /* X * We have just barely enough room to X * exit underline mode and handle a possible X * underline/boldface run on mixup. X */ X return (1); X ln_state = LN_UL_X; X break; X case LN_BOLDFACE: X if (c == '\b') X { X ln_state = LN_BO_XB; X break; X } X if (column + be_width + ul_width + 1 + ue_width >= sc_width) X /* X * We have just barely enough room to X * exit underline mode and handle a possible X * underline/boldface run on mixup. X */ X return (1); X ln_state = LN_BO_X; X break; X case LN_UL_X: X if (c == '\b') X ln_state = LN_UL_XB; X else X { X /* X * Exit underline mode. X * We have to shuffle the chars a bit X * to make this work. X */ X curr[0] = curr[-1]; X curr[-1] = UE_CHAR; X column += ue_width; X if (ue_width > 0 && curr[0] == ' ') X /* X * Another special case for magic X * cookie terminals: if the next X * char is a space, replace it X * with the "exit underline" sequence. X */ X column--; X else X curr++; X ln_state = LN_NORMAL; X } X break; X case LN_BO_X: X if (c == '\b') X ln_state = LN_BO_XB; X else X { X /* X * Exit boldface mode. X * We have to shuffle the chars a bit X * to make this work. X */ X curr[0] = curr[-1]; X curr[-1] = BE_CHAR; X column += be_width; X if (be_width > 0 && curr[0] == ' ') X /* X * Another special case for magic X * cookie terminals: if the next X * char is a space, replace it X * with the "exit boldface" sequence. X */ X column--; X else X curr++; X ln_state = LN_NORMAL; X } X break; X } X } X X if (c == '\t') X { X /* X * Expand a tab into spaces. X */ X do X { X NEW_COLUMN(column+1); X } while ((column % tabstop) != 0); X *curr++ = '\t'; X return (0); X } X X if (c == '\b') X { X if (bs_mode == BS_CONTROL) X { X /* X * Treat backspace as a control char: output "^H". X */ X NEW_COLUMN(column+2); X *curr++ = ('H' | 0200); X } else X { X /* X * Output a real backspace. X */ X column--; X *curr++ = '\b'; X } X return (0); X } X X if (control_char(c)) X { X /* X * Put a "^X" into the buffer. X * The 0200 bit is used to tell put_line() to prefix X * the char with a ^. We don't actually put the ^ X * in the buffer because we sometimes need to move X * chars around, and such movement might separate X * the ^ from its following character. X * {{ This should be redone so that we can use an X * 8 bit (e.g. international) character set. }} X */ X NEW_COLUMN(column+2); X *curr++ = (carat_char(c) | 0200); X return (0); X } X X /* X * Ordinary character. Just put it in the buffer. X */ X NEW_COLUMN(column+1); X *curr++ = c; X return (0); X} X X/* X * Analogous to forw_line(), but deals with "raw lines": X * lines which are not split for screen width. X * {{ This is supposed to be more efficient than forw_line(). }} X */ X public POSITION Xforw_raw_line(curr_pos) X POSITION curr_pos; X{ X register char *p; X register int c; X POSITION new_pos; X X if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || X (c = ch_forw_get()) == EOF) X return (NULL_POSITION); X X p = linebuf; X X for (;;) X { X if (c == '\n' || c == EOF) X { X new_pos = ch_tell(); X break; X } X if (p >= &linebuf[sizeof(linebuf)-1]) X { X /* X * Overflowed the input buffer. X * Pretend the line ended here. X * {{ The line buffer is supposed to be big X * enough that this never happens. }} X */ X new_pos = ch_tell() - 1; X break; X } X *p++ = c; X c = ch_forw_get(); X } X *p = '\0'; X line = linebuf; X return (new_pos); X} X X/* X * Analogous to back_line(), but deals with "raw lines". X * {{ This is supposed to be more efficient than back_line(). }} X */ X public POSITION Xback_raw_line(curr_pos) X POSITION curr_pos; X{ X register char *p; X register int c; X POSITION new_pos; X X if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 || X ch_seek(curr_pos-1)) X return (NULL_POSITION); X X p = &linebuf[sizeof(linebuf)]; X *--p = '\0'; X X for (;;) X { X c = ch_back_get(); X if (c == '\n') X { X /* X * This is the newline ending the previous line. X * We have hit the beginning of the line. X */ X new_pos = ch_tell() + 1; X break; X } X if (c == EOF) X { X /* X * We have hit the beginning of the file. X * This must be the first line in the file. X * This must, of course, be the beginning of the line. X */ X new_pos = (POSITION)0; X break; X } X if (p <= linebuf) X { X /* X * Overflowed the input buffer. X * Pretend the line ended here. X */ X new_pos = ch_tell() + 1; X break; X } X *--p = c; X } X line = p; X return (new_pos); X} SHAR_EOF fi if test -f 'main.c' then echo shar: "will not over-write existing file 'main.c'" else sed 's/^X//' << \SHAR_EOF > 'main.c' X/* X * Entry point, initialization, miscellaneous routines. X */ X X#include "less.h" X#include "position.h" X#include X Xpublic int ispipe; Xpublic jmp_buf main_loop; Xpublic char * first_cmd; Xpublic char * every_first_cmd; Xpublic int new_file; Xpublic int is_tty; Xpublic char current_file[128]; Xpublic int any_display; Xpublic int ac; Xpublic char ** av; Xpublic int curr_ac; X#if LOGFILE Xpublic int logfile = -1; Xpublic char * namelogfile = NULL; X#endif X#if EDITOR Xpublic char * editor; X#endif X Xextern int file; Xextern int nbufs; Xextern int sigs; Xextern int quit_at_eof; Xextern int p_nbufs, f_nbufs; Xextern int back_scroll; Xextern int top_scroll; Xextern int sc_height; Xextern int errmsgs; X X X/* X * Edit a new file. X * Filename "-" means standard input. X * No filename means the "current" file, from the command line. X */ X public void Xedit(filename) X char *filename; X{ X register int f; X char message[100]; X static didpipe; X X if (filename == NULL || *filename == '\0') X { X if (curr_ac >= ac) X { X error("No current file"); X return; X } X filename = av[curr_ac]; X } X if (strcmp(filename, "-") == 0) X { X /* X * Use standard input. X */ X if (didpipe) X { X error("Can view standard input only once"); X return; X } X f = 0; X } else if ((f = open(filename, 0)) < 0) X { X static char co[] = "Cannot open "; X strcpy(message, co); X strtcpy(message+sizeof(co)-1, filename, X sizeof(message)-sizeof(co)); X error(message); X return; X } X X if (isatty(f)) X { X /* X * Not really necessary to call this an error, X * but if the control terminal (for commands) X * and the input file (for data) are the same, X * we get weird results at best. X */ X error("Can't take input from a terminal"); X if (f > 0) X close(f); X return; X } X X#if LOGFILE X /* X * If he asked for a log file and we have opened standard input, X * create the log file. X * We take care not to blindly overwrite an existing file. X */ X end_logfile(); X if (f == 0 && namelogfile != NULL && is_tty) X { X int exists; X int answer; X X /* X * {{ We could use access() here. }} X */ X exists = open(namelogfile, 0); X close(exists); X exists = (exists >= 0); X X if (exists) X { X static char w[] = "WARNING: log file exists: "; X strcpy(message, w); X strtcpy(message+sizeof(w)-1, namelogfile, X sizeof(message)-sizeof(w)); X error(message); X answer = 'X'; /* Ask the user what to do */ X } else X answer = 'O'; /* Create the log file */ X X loop: X switch (answer) X { X case 'O': case 'o': X logfile = creat(namelogfile, 0644); X break; X case 'A': case 'a': X logfile = open(namelogfile, 1); X if (lseek(logfile, (off_t)0, 2) < 0) X { X close(logfile); X logfile = -1; X } X break; X case 'D': case 'd': X answer = 0; /* Don't print an error message */ X break; X case 'q': X quit(); X default: X puts("\n Overwrite, Append, or Don't log? "); X answer = getc(); X puts("\n"); X flush(); X goto loop; X } X X if (logfile < 0 && answer != 0) X { X sprintf(message, "Cannot write to \"%s\"", X namelogfile); X error(message); X } X } X#endif X X /* X * We are now committed to using the new file. X * Close the current input file and set up to use the new one. X */ X if (file > 0) X close(file); X new_file = 1; X strtcpy(current_file, filename, sizeof(current_file)); X ispipe = (f == 0); X if (ispipe) X didpipe = 1; X file = f; X ch_init( (ispipe) ? p_nbufs : f_nbufs ); X init_mark(); X X if (every_first_cmd != NULL) X first_cmd = every_first_cmd; X X if (is_tty) X { X int no_display = !any_display; X any_display = 1; X if (no_display && errmsgs > 0) X { X /* X * We displayed some messages on error output X * (file descriptor 2; see error() function). X * Before erasing the screen contents, X * display the file name and wait for a keystroke. X */ X error(filename); X } X /* X * Indicate there is nothing displayed yet. X */ X pos_clear(); X } X} X X/* X * Edit the next file in the command line list. X */ X public void Xnext_file(n) X int n; X{ X if (curr_ac + n >= ac) X { X if (quit_at_eof) X quit(); X error("No (N-th) next file"); X } else X edit(av[curr_ac += n]); X} X X/* X * Edit the previous file in the command line list. X */ X public void Xprev_file(n) X int n; X{ X if (curr_ac - n < 0) X error("No (N-th) previous file"); X else X edit(av[curr_ac -= n]); X} X X/* X * Copy a file directly to standard output. X * Used if standard output is not a tty. X */ X static void Xcat_file() X{ X register int c; X X while ((c = ch_forw_get()) != EOF) X putc(c); X flush(); X} X X/* X * Entry point. X */ Xmain(argc, argv) X int argc; X char *argv[]; X{ X char *getenv(); X X X /* X * Process command line arguments and LESS environment arguments. X * Command line arguments override environment arguments. X */ X init_option(); X scan_option(getenv("LESS")); X argv++; X while ( (--argc > 0) && X (argv[0][0] == '-' || argv[0][0] == '+') && X argv[0][1] != '\0') X scan_option(*argv++); X X#if EDITOR X editor = getenv("EDITOR"); X if (editor == NULL || *editor == '\0') X editor = EDIT_PGM; X#endif X X /* X * Set up list of files to be examined. X */ X ac = argc; X av = argv; X curr_ac = 0; X X /* X * Set up terminal, etc. X */ X is_tty = isatty(1); X if (!is_tty) X { X /* X * Output is not a tty. X * Just copy the input file(s) to output. X */ X if (ac < 1) X { X edit("-"); X cat_file(); X } else X { X do X { X edit((char *)NULL); X if (file >= 0) X cat_file(); X } while (++curr_ac < ac); X } X exit(0); X } X X raw_mode(1); X get_term(); X open_getc(); X init(); X X if (setjmp(main_loop)) X quit(); X init_signals(); X X /* X * Select the first file to examine. X */ X if (ac < 1) X edit("-"); /* Standard input */ X else X { X /* X * Try all the files named as command arguments. X * We are simply looking for one which can be X * opened without error. X */ X do X { X edit((char *)NULL); X } while (file < 0 && ++curr_ac < ac); X } X X if (file >= 0) X commands(); X quit(); X} X X/* X * Copy a string, truncating to the specified length if necessary. X * Unlike strncpy(), the resulting string is guaranteed to be null-terminated. X */ Xstrtcpy(to, from, len) X char *to; X char *from; X int len; X{ X strncpy(to, from, len); X to[len-1] = '\0'; X} X X/* X * Exit the program. X */ X public void Xquit() X{ X /* X * Put cursor at bottom left corner, clear the line, X * reset the terminal modes, and exit. X */ X#if LOGFILE X end_logfile(); X#endif X lower_left(); X clear_eol(); X deinit(); X flush(); X raw_mode(0); X exit(0); X} SHAR_EOF fi if test -f 'makefile' then echo shar: "will not over-write existing file 'makefile'" else sed 's/^X//' << \SHAR_EOF > 'makefile' X# Makefile for "less" X# X# Invoked as: X# make all X# or make install X# Plain "make" is equivalent to "make all". X# X# If you add or delete functions, remake funcs.h by doing: X# make newfuncs X# This depends on the coding convention of function headers looking like: X# " \t public \n ( ... ) " X# X# Also provided: X# make lint # Runs "lint" on all the sources. X# make clean # Removes "less" and the .o files. X# make clobber # Pretty much the same as make "clean". X X X########################################################################## X# System-specific parameters X########################################################################## X X# Define XENIX if running under XENIX 3.0 XXENIX = 0 X X# VOID is 1 if your C compiler supports the "void" type, X# 0 if it does not. XVOID = 1 X X# off_t is the type which lseek() returns. X# It is also the type of lseek()'s second argument. Xoff_t = long X X# TERMIO is 1 if your system has /usr/include/termio.h. X# This is normally the case for System 5. X# If TERMIO is 0 your system must have /usr/include/sgtty.h. X# This is normally the case for BSD. XTERMIO = 0 X X# SIGSETMASK is 1 if your system has the sigsetmask() call. X# This is normally the case only for BSD 4.2, X# not for BSD 4.1 or System 5. XSIGSETMASK = 1 X X########################################################################## X# Optional and semi-optional features X########################################################################## X X# REGCMP is 1 if your system has the regcmp() function. X# This is normally the case for System 5. X# RECOMP is 1 if your system has the re_comp() function. X# This is normally the case for BSD. X# If neither is 1, pattern matching is supported, but without metacharacters. XREGCMP = 0 XRECOMP = 1 X X# SHELL_ESCAPE is 1 if you wish to allow shell escapes. X# (This is possible only if your system supplies the system() function.) XSHELL_ESCAPE = 1 X X# EDITOR is 1 if you wish to allow editor invocation (the "v" command). X# (This is possible only if your system supplies the system() function.) X# EDIT_PGM is the name of the (default) editor to be invoked. XEDITOR = 1 XEDIT_PGM = vi X X# GLOB is 1 if you wish to have shell metacharacters expanded in filenames. X# This will generally work if your system provides the "popen" function X# and the "echo" shell command. XGLOB = 1 X X# LOGFILE is 1 if you wish to allow the -l option (to create log files). XLOGFILE = 1 X X# ONLY_RETURN is 1 if you want RETURN to be the only input which X# will continue past an error message. X# Otherwise, any key will continue past an error message. XONLY_RETURN = 0 X X X########################################################################## X# Compilation environment. X########################################################################## X X# LIBS is the list of libraries needed. XLIBS = -ltermcap X X# INSTALL_LESS is a list of the public versions of less. X# INSTALL_HELP is a list of the public version of the help file. X# INSTALL_MAN is a list of the public versions of the manual page. XINSTALL_LESS = /usr/local/less XINSTALL_HELP = /usr/local/lib/less.help XINSTALL_MAN = /usr/local/man/cat1/less.0 XMANUAL = less.nro XHELPFILE = /usr/local/lib/less.help X X X# OPTIM is passed to the compiler and the loader. X# It is normally "-O" but may be, for example, "-g". XOPTIM = -O -i X X X########################################################################## X# Files X########################################################################## X XSRC1 = main.c option.c prim.c ch.c position.c input.c output.c XSRC2 = screen.c prompt.c line.c signal.c help.c ttyin.c command.c version.c XSRC = $(SRC1) $(SRC2) XOBJ = main.o option.o prim.o ch.o position.o input.o output.o screen.o \ X prompt.o line.o signal.o help.o ttyin.o command.o version.o X X X########################################################################## X# Rules X########################################################################## X XDEFS = "-DTERMIO=$(TERMIO)" \ X "-DSIGSETMASK=$(SIGSETMASK)" \ X "-Doff_t=$(off_t)" "-DVOID=$(VOID)" \ X "-DREGCMP=$(REGCMP)" "-DRECOMP=$(RECOMP)" \ X "-DSHELL_ESCAPE=$(SHELL_ESCAPE)" \ X "-DEDITOR=$(EDITOR)" "-DEDIT_PGM=\"$(EDIT_PGM)\"" \ X "-DHELPFILE=\"$(HELPFILE)\"" \ X "-DLOGFILE=$(LOGFILE)" \ X "-DONLY_RETURN=$(ONLY_RETURN)" \ X "-DGLOB=$(GLOB)" \ X "-DXENIX=$(XENIX)" X XCFLAGS = $(OPTIM) $(DEFS) X X Xall: less X Xless: $(OBJ) X cc $(OPTIM) -o less $(OBJ) $(LIBS) X Xinstall: install_man install_less install_help X Xinstall_less: less X for f in $(INSTALL_LESS); do rm -f $$f; cp less $$f; done X touch install_less X Xinstall_help: less.help X for f in $(INSTALL_HELP); do rm -f $$f; cp less.help $$f; done X touch install_help X Xinstall_man: $(MANUAL) X rm -f $(INSTALL_MAN) X /usr/man/manroff $(MANUAL) > $(INSTALL_MAN) X chmod 444 $(INSTALL_MAN) X chown bin.bin $(INSTALL_MAN) X touch install_man X X$(OBJ): less.h funcs.h X X# help.o depends on makefile for the definition of HELPFILE. Xhelp.o: makefile X Xlint: X lint -hp $(DEFS) $(SRC) X Xnewfuncs: X mv funcs.h funcs.h.OLD X awk -f mkfuncs.awk $(SRC) >funcs.h X Xclean: X rm -f $(OBJ) less install_help install_less install_man core X Xclobber: X rm -f *.o less install_less install_man X Xshar: X shar -v README install less.man less.help makefile.* *.h *.awk > less.shar.a X shar -v less.nro $(SRC1) > less.shar.b X shar -v $(SRC2) > less.shar.c SHAR_EOF fi if test -f 'mkfuncs.awk' then echo shar: "will not over-write existing file 'mkfuncs.awk'" else sed 's/^X//' << \SHAR_EOF > 'mkfuncs.awk' XBEGIN { FS="("; state = 0 } X X/^ public/ { ftype = $0; state = 1 } X X{ if (state == 1) X state = 2 X else if (state == 2) X { print ftype,$1,"();"; state = 0 } X} SHAR_EOF fi if test -f 'option.c' then echo shar: "will not over-write existing file 'option.c'" else sed 's/^X//' << \SHAR_EOF > 'option.c' X/* X * Process command line options. X * Each option is a single letter which controls a program variable. X * The options have defaults which may be changed via X * the command line option, or toggled via the "-" command. X */ X X#include "less.h" X X#define toupper(c) ((c)-'a'+'A') X X#define END_OPTION_STRING ('$') X X/* X * Types of options. X */ X#define BOOL 01 /* Boolean option: 0 or 1 */ X#define TRIPLE 02 /* Triple-valued option: 0, 1 or 2 */ X#define NUMBER 04 /* Numeric option */ X#define REPAINT 040 /* Repaint screen after toggling option */ X#define NO_TOGGLE 0100 /* Option cannot be toggled with "-" cmd */ X X/* X * Variables controlled by command line options. X */ Xpublic int p_nbufs, f_nbufs; /* Number of buffers. There are two values, X one used for input from a pipe and X the other for input from a file. */ Xpublic int clean_data; /* Can we assume the data is "clean"? X (That is, free of nulls, etc) */ Xpublic int quiet; /* Should we suppress the audible bell? */ Xpublic int top_search; /* Should forward searches start at the top X of the screen? (alternative is bottom) */ Xpublic int top_scroll; /* Repaint screen from top? X (alternative is scroll from bottom) */ Xpublic int pr_type; /* Type of prompt (short, medium, long) */ Xpublic int bs_mode; /* How to process backspaces */ Xpublic int know_dumb; /* Don't complain about dumb terminals */ Xpublic int quit_at_eof; /* Quit after hitting end of file twice */ Xpublic int squeeze; /* Squeeze multiple blank lines into one */ Xpublic int tabstop; /* Tab settings */ Xpublic int back_scroll; /* Repaint screen on backwards movement */ Xpublic int twiddle; /* Display "~" for lines after EOF */ X Xextern char *prproto[]; Xextern int nbufs; Xextern int sc_window; Xextern char *first_cmd; Xextern char *every_first_cmd; X#if LOGFILE Xextern char *namelogfile; X#endif X X#define DEF_F_NBUFS 5 /* Default for f_nbufs */ X#define DEF_P_NBUFS 12 /* Default for p_nbufs */ X Xstatic struct option X{ X char oletter; /* The controlling letter (a-z) */ X char otype; /* Type of the option */ X int odefault; /* Default value */ X int *ovar; /* Pointer to the associated variable */ X char *odesc[3]; /* Description of each value */ X} option[] = X{ X { 'c', BOOL, 0, &clean_data, X { "Don't assume data is clean", X "Assume data is clean", X NULL X } X }, X { 'd', BOOL|NO_TOGGLE, 0, &know_dumb, X { NULL, NULL, NULL} X }, X { 'e', BOOL, 0, &quit_at_eof, X { "Don't quit at end-of-file", X "Quit at end-of-file", X NULL X } X }, X { 'h', NUMBER, -1, &back_scroll, X { "Backwards scroll limit is %d lines", X NULL, NULL X } X }, X { 'p', BOOL, 0, &top_scroll, X { "Repaint by scrolling from bottom of screen", X "Repaint by painting from top of screen", X NULL X } X }, X { 'x', NUMBER, 8, &tabstop, X { "Tab stops every %d spaces", X NULL, NULL X } X }, X { 's', BOOL|REPAINT, 0, &squeeze, X { "Don't squeeze multiple blank lines", X "Squeeze multiple blank lines", X NULL X } X }, X { 't', BOOL, 1, &top_search, X { "Forward search starts from bottom of screen", X "Forward search starts from top of screen", X NULL X } X }, X { 'w', BOOL|REPAINT, 1, &twiddle, X { "Display nothing for lines after end-of-file", X "Display ~ for lines after end-of-file", X NULL X } X }, X { 'm', TRIPLE, 0, &pr_type, X { "Short prompt", X "Medium prompt", X "Long prompt" X } X }, X { 'q', TRIPLE, 0, &quiet, X { "Ring the bell for errors AND at eof/bof", X "Ring the bell for errors but not at eof/bof", X "Never ring the bell" X } X }, X { 'u', TRIPLE|REPAINT, 0, &bs_mode, X { "Underlined text displayed in underline mode", X "Backspaces cause overstrike", X "Backspaces print as ^H" X } X }, X { 'z', NUMBER, 24, &sc_window, X { "Scroll window size is %d lines", X NULL, NULL X } X }, X { '\0' } X}; X Xpublic char all_options[64]; /* List of all valid options */ X X/* X * Initialize each option to its default value. X */ X public void Xinit_option() X{ X register struct option *o; X register char *p; X X /* X * First do special cases, not in option table. X */ X first_cmd = every_first_cmd = NULL; X f_nbufs = DEF_F_NBUFS; /* -bf */ X p_nbufs = DEF_P_NBUFS; /* -bp */ X X p = all_options; X *p++ = 'b'; X X for (o = option; o->oletter != '\0'; o++) X { X /* X * Set each variable to its default. X * Also make a list of all options, in "all_options". X */ X *(o->ovar) = o->odefault; X *p++ = o->oletter; X if (o->otype & TRIPLE) X *p++ = toupper(o->oletter); X } X *p = '\0'; X} X X/* X * Toggle command line flags from within the program. X * Used by the "-" command. X */ X public void Xtoggle_option(c) X int c; X{ X register struct option *o; X char message[100]; X char buf[5]; X X /* X * First check for special cases not handled by the option table. X */ X switch (c) X { X case 'b': X sprintf(message, "%d buffers", nbufs); X error(message); X return; X } X X X for (o = option; o->oletter != '\0'; o++) X { X if (o->otype & NO_TOGGLE) X continue; X if ((o->otype & BOOL) && (o->oletter == c)) X { X /* X * Boolean option: X * just toggle it. X */ X *(o->ovar) = ! *(o->ovar); X } else if ((o->otype & TRIPLE) && (o->oletter == c)) X { X /* X * Triple-valued option with lower case letter: X * make it 1 unless already 1, then make it 0. X */ X *(o->ovar) = (*(o->ovar) == 1) ? 0 : 1; X } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c)) X { X /* X * Triple-valued option with upper case letter: X * make it 2 unless already 2, then make it 0. X */ X *(o->ovar) = (*(o->ovar) == 2) ? 0 : 2; X } else if ((o->otype & NUMBER) && (o->oletter == c)) X { X sprintf(message, o->odesc[0], X (o->ovar == &back_scroll) ? X get_back_scroll() : *(o->ovar)); X error(message); X return; X } else X continue; X X if (o->otype & REPAINT) X repaint(); X error(o->odesc[*(o->ovar)]); X return; X } X X if (control_char(c)) X sprintf(buf, "^%c", carat_char(c)); X else X sprintf(buf, "%c", c); X sprintf(message, "\"-%s\": no such flag. Use one of \"%s\"", X buf, all_options); X error(message); X} X X/* X * Scan to end of string or to an END_OPTION_STRING character. X * In the latter case, replace the char with a null char. X * Return a pointer to the remainder of the string, if any. X */ X static char * Xoptstring(s) X char *s; X{ X register char *p; X X for (p = s; *p != '\0'; p++) X if (*p == END_OPTION_STRING) X { X *p = '\0'; X return (p+1); X } X return (p); X} X X/* X * Scan an argument (either from command line or from LESS environment X * variable) and process it. X */ X public void Xscan_option(s) X char *s; X{ X register struct option *o; X register int c; X char message[80]; X X if (s == NULL) X return; X X next: X if (*s == '\0') X return; X switch (c = *s++) X { X case '-': X case ' ': X case '\t': X case END_OPTION_STRING: X goto next; X case '+': X if (*s == '+') X every_first_cmd = ++s; X first_cmd = s; X s = optstring(s); X goto next; X case 'P': X switch (*s) X { X case 'm': prproto[PR_MEDIUM] = ++s; break; X case 'M': prproto[PR_LONG] = ++s; break; X default: prproto[PR_SHORT] = s; break; X } X s = optstring(s); X goto next; X#if LOGFILE X case 'l': X namelogfile = s; X s = optstring(s); X goto next; X#endif X case 'b': X switch (*s) X { X case 'f': X s++; X f_nbufs = getnum(&s, 'b'); X break; X case 'p': X s++; X p_nbufs = getnum(&s, 'b'); X break; X default: X f_nbufs = p_nbufs = getnum(&s, 'b'); X break; X } X goto next; X case '0': case '1': case '2': case '3': case '4': X case '5': case '6': case '7': case '8': case '9': X { X /* X * Handle special "more" compatibility form "-number" X * to set the scrolling window size. X */ X s--; X sc_window = getnum(&s, '-'); X goto next; X } X } X X for (o = option; o->oletter != '\0'; o++) X { X if ((o->otype & BOOL) && (o->oletter == c)) X { X *(o->ovar) = ! o->odefault; X goto next; X } else if ((o->otype & TRIPLE) && (o->oletter == c)) X { X *(o->ovar) = (o->odefault == 1) ? 0 : 1; X goto next; X } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c)) X { X *(o->ovar) = (o->odefault == 2) ? 0 : 2; X goto next; X } else if ((o->otype & NUMBER) && (o->oletter == c)) X { X *(o->ovar) = getnum(&s, c); X goto next; X } X } X X sprintf(message, "\"-%c\": invalid flag", c); X error(message); X exit(1); X} X X/* X * Translate a string into a number. X * Like atoi(), but takes a pointer to a char *, and updates X * the char * to point after the translated number. X */ X static int Xgetnum(sp, c) X char **sp; X int c; X{ X register char *s; X register int n; X char message[80]; X X s = *sp; X if (*s < '0' || *s > '9') X { X sprintf(message, "number is required after -%c", c); X error(message); X exit(1); X } X X n = 0; X while (*s >= '0' && *s <= '9') X n = 10 * n + *s++ - '0'; X *sp = s; X return (n); X} SHAR_EOF fi if test -f 'output.c' then echo shar: "will not over-write existing file 'output.c'" else sed 's/^X//' << \SHAR_EOF > 'output.c' X/* X * High level routines dealing with the output to the screen. X */ X X#include "less.h" X Xpublic int errmsgs; /* Count of messages displayed by error() */ X Xextern int sigs; Xextern int sc_width, sc_height; Xextern int ul_width, ue_width; Xextern int so_width, se_width; Xextern int bo_width, be_width; Xextern int tabstop; Xextern int twiddle; Xextern int any_display; Xextern char *line; Xextern char *first_cmd; X X/* X * Display the line which is in the line buffer. X */ X public void Xput_line() X{ X register char *p; X register int c; X register int column; X extern int auto_wrap, ignaw; X X if (sigs) X /* X * Don't output if a signal is pending. X */ X return; X X if (line == NULL) X line = (twiddle) ? "~" : ""; X X column = 0; X for (p = line; *p != '\0'; p++) X { X switch (c = *p) X { X case UL_CHAR: X ul_enter(); X column += ul_width; X break; X case UE_CHAR: X ul_exit(); X column += ue_width; X break; X case BO_CHAR: X bo_enter(); X column += bo_width; X break; X case BE_CHAR: X bo_exit(); X column += be_width; X break; X case '\t': X do X { X putc(' '); X column++; X } while ((column % tabstop) != 0); X break; X case '\b': X putbs(); X column--; X break; X default: X if (c & 0200) X { X putc('^'); X putc(c & 0177); X column += 2; X } else X { X putc(c); X column++; X } X } X } X if (column < sc_width || !auto_wrap || ignaw) X putc('\n'); X} X X/* X * Is a given character a "control" character? X * {{ ASCII DEPENDENT }} X */ X public int Xcontrol_char(c) X int c; X{ X return (c < ' ' || c == '\177'); X} X X/* X * Return the printable character used to identify a control character X * (printed after a carat; e.g. '\3' => "^C"). X * {{ ASCII DEPENDENT }} X */ X public int Xcarat_char(c) X int c; X{ X return ((c == '\177') ? '?' : (c | 0100)); X} X X Xstatic char obuf[1024]; Xstatic char *ob = obuf; X X/* X * Flush buffered output. X */ X public void Xflush() X{ X write(1, obuf, ob-obuf); X ob = obuf; X} X X/* X * Discard buffered output. X */ X public void Xdropout() X{ X ob = obuf; X} X X/* X * Output a character. X */ X public void Xputc(c) X int c; X{ X if (ob >= &obuf[sizeof(obuf)]) X flush(); X *ob++ = c; X} X X/* X * Output a string. X */ X public void Xputs(s) X register char *s; X{ X while (*s != '\0') X putc(*s++); X} X X/* X * Output a message in the lower left corner of the screen X * and wait for carriage return. X */ X Xstatic char return_to_continue[] = " (press RETURN)"; X X public void Xerror(s) X char *s; X{ X register int c; X static char buf[2]; X X errmsgs++; X if (!any_display) X { X /* X * Nothing has been displayed yet. X * Output this message on error output (file X * descriptor 2) and don't wait for a keystroke X * to continue. X * X * This has the desirable effect of producing all X * error messages on error output if standard output X * is directed to a file. It also does the same if X * we never produce any real output; for example, if X * the input file(s) cannot be opened. If we do X * eventually produce output, code in edit() makes X * sure these messages can be seen before they are X * overwritten or scrolled away. X */ X write(2, s, strlen(s)); X write(2, "\n", 1); X return; X } X X lower_left(); X clear_eol(); X so_enter(); X puts(s); X puts(return_to_continue); X so_exit(); X X#if ONLY_RETURN X while ((c = getc()) != '\n' && c != '\r') X bell(); X#else X c = getc(); X if (c != '\n' && c != '\r' && c != ' ') X { X buf[0] = c; X first_cmd = buf; X } X#endif X X if (strlen(s) + sizeof(return_to_continue) + X so_width + se_width + 1 > sc_width) X /* X * Printing the message has probably scrolled the screen. X * {{ Unless the terminal doesn't have auto margins, X * in which case we just hammered on the right margin. }} X */ X repaint(); X} X X#ifdef notdef X public int Xerror_width() X{ X /* X * Don't use the last position, because some terminals X * will scroll if you write in the last char of the last line. X */ X return (sc_width - X (sizeof(return_to_continue) + so_width + se_width + 1)); X} X#endif SHAR_EOF fi if test -f 'position.c' then echo shar: "will not over-write existing file 'position.c'" else sed 's/^X//' << \SHAR_EOF > 'position.c' X/* X * Routines dealing with the "position" table. X * This is a table which tells the position (in the input file) of the X * first char on each currently displayed line. X * X * {{ The position table is scrolled by moving all the entries. X * Would be better to have a circular table X * and just change a couple of pointers. }} X */ X X#include "less.h" X#include "position.h" X X#define NPOS 100 /* {{ sc_height must be less than NPOS }} */ Xstatic POSITION table[NPOS]; /* The position table */ X Xextern int sc_width, sc_height; X X/* X * Return the starting file position of a line displayed on the screen. X * The line may be specified as a line number relative to the top X * of the screen, but is usually one of these special cases: X * the top (first) line on the screen X * the second line on the screen X * the bottom line on the screen X * the line after the bottom line on the screen X */ X public POSITION Xposition(where) X int where; X{ X switch (where) X { X case BOTTOM: X where = sc_height - 2; X break; X case BOTTOM_PLUS_ONE: X where = sc_height - 1; X break; X } X return (table[where]); X} X X/* X * Add a new file position to the bottom of the position table. X */ X public void Xadd_forw_pos(pos) X POSITION pos; X{ X register int i; X X /* X * Scroll the position table up. X */ X for (i = 1; i < sc_height; i++) X table[i-1] = table[i]; X table[sc_height - 1] = pos; X} X X/* X * Add a new file position to the top of the position table. X */ X public void Xadd_back_pos(pos) X POSITION pos; X{ X register int i; X X /* X * Scroll the position table down. X */ X for (i = sc_height - 1; i > 0; i--) X table[i] = table[i-1]; X table[0] = pos; X} X X/* X * Initialize the position table, done whenever we clear the screen. X */ X public void Xpos_clear() X{ X register int i; X X for (i = 0; i < sc_height; i++) X table[i] = NULL_POSITION; X} X X/* X * See if the byte at a specified position is currently on the screen. X * Check the position table to see if the position falls within its range. X * Return the position table entry if found, -1 if not. X */ X public int Xonscreen(pos) X POSITION pos; X{ X register int i; X X if (pos < table[0]) X return (-1); X for (i = 1; i < sc_height; i++) X if (pos < table[i]) X return (i-1); X return (-1); X} SHAR_EOF fi if test -f 'position.h' then echo shar: "will not over-write existing file 'position.h'" else sed 's/^X//' << \SHAR_EOF > 'position.h' X/* X * Include file for interfacing to position.c modules. X */ X#define TOP 0 X#define TOP_PLUS_ONE 1 X#define BOTTOM -1 X#define BOTTOM_PLUS_ONE -2 SHAR_EOF fi if test -f 'prim.c' then echo shar: "will not over-write existing file 'prim.c'" else sed 's/^X//' << \SHAR_EOF > 'prim.c' X/* X * Primitives for displaying the file on the screen. X */ X X#include "less.h" X#include "position.h" X Xpublic int hit_eof; /* Keeps track of how many times we hit end of file */ X Xextern int quiet; Xextern int top_search; Xextern int top_scroll; Xextern int back_scroll; Xextern int sc_width, sc_height; Xextern int sigs; Xextern char *line; Xextern char *first_cmd; X X/* X * Sound the bell to indicate he is trying to move past end of file. X */ X static void Xeof_bell() X{ X if (quiet == NOT_QUIET) X bell(); X else X vbell(); X} X X/* X * Check to see if the end of file is currently "displayed". X */ X static void Xeof_check() X{ X POSITION pos; X X /* X * If the bottom line is empty, we are at EOF. X * If the bottom line ends at the file length, X * we must be just at EOF. X */ X pos = position(BOTTOM_PLUS_ONE); X if (pos == NULL_POSITION || pos == ch_length()) X hit_eof++; X} X X/* X * Display n lines, scrolling forward, X * starting at position pos in the input file. X * "force" means display the n lines even if we hit end of file. X * "only_last" means display only the last screenful if n > screen size. X */ X static void Xforw(n, pos, force, only_last) X register int n; X POSITION pos; X int force; X int only_last; X{ X int eof = 0; X int nlines = 0; X int repaint_flag; X static int first_time = 1; X X /* X * repaint_flag tells us not to display anything till the end, X * then just repaint the entire screen. X */ X repaint_flag = (only_last && n > sc_height-1); X X if (!repaint_flag) X { X if (top_scroll && n >= sc_height - 1) X { X /* X * Start a new screen. X * {{ This is not really desirable if we happen X * to hit eof in the middle of this screen, X * but we don't yet know if that will happen. }} X */ X clear(); X home(); X force = 1; X } else X { X lower_left(); X clear_eol(); X } X X if (pos != position(BOTTOM_PLUS_ONE)) X { X /* X * This is not contiguous with what is X * currently displayed. Clear the screen image X * (position table) and start a new screen. X */ X pos_clear(); X add_forw_pos(pos); X force = 1; X if (top_scroll) X { X clear(); X home(); X } else if (!first_time) X { X puts("...skipping...\n"); X } X } X } X X while (--n >= 0) X { X /* X * Read the next line of input. X */ X pos = forw_line(pos); X if (pos == NULL_POSITION) X { X /* X * End of file: stop here unless the top line X * is still empty, or "force" is true. X */ X eof = 1; X if (!force && position(TOP) != NULL_POSITION) X break; X line = NULL; X } X /* X * Add the position of the next line to the position table. X * Display the current line on the screen. X */ X add_forw_pos(pos); X nlines++; X if (repaint_flag || X (first_time && line == NULL && !top_scroll)) X continue; X put_line(); X } X X if (eof) X hit_eof++; X else X eof_check(); X if (nlines == 0) X eof_bell(); X else if (repaint_flag) X repaint(); X first_time = 0; X} X X/* X * Display n lines, scrolling backward. X */ X static void Xback(n, pos, force, only_last) X register int n; X POSITION pos; X int force; X int only_last; X{ X int nlines = 0; X int repaint_flag; X X repaint_flag = (n > get_back_scroll() || (only_last && n > sc_height-1)); X hit_eof = 0; X while (--n >= 0) X { X /* X * Get the previous line of input. X */ X pos = back_line(pos); X if (pos == NULL_POSITION) X { X /* X * Beginning of file: stop here unless "force" is true. X */ X if (!force) X break; X line = NULL; X } X /* X * Add the position of the previous line to the position table. X * Display the line on the screen. X */ X add_back_pos(pos); X nlines++; X if (!repaint_flag) X { X home(); X add_line(); X put_line(); X } X } X X eof_check(); X if (nlines == 0) X eof_bell(); X else if (repaint_flag) X repaint(); X} X X/* X * Display n more lines, forward. X * Start just after the line currently displayed at the bottom of the screen. X */ X public void Xforward(n, only_last) X int n; X int only_last; X{ X POSITION pos; X X pos = position(BOTTOM_PLUS_ONE); X if (pos == NULL_POSITION) X { X eof_bell(); X hit_eof++; X return; X } X forw(n, pos, 0, only_last); X} X X/* X * Display n more lines, backward. X * Start just before the line currently displayed at the top of the screen. X */ X public void Xbackward(n, only_last) X int n; X int only_last; X{ X POSITION pos; X X pos = position(TOP); X if (pos == NULL_POSITION) X { X /* X * This will almost never happen, X * because the top line is almost never empty. X */ X eof_bell(); X return; X } X back(n, pos, 0, only_last); X} X X/* X * Repaint the screen, starting from a specified position. X */ X static void Xprepaint(pos) X POSITION pos; X{ X hit_eof = 0; X forw(sc_height-1, pos, 0, 0); X} X X/* X * Repaint the screen. X */ X public void Xrepaint() X{ X /* X * Start at the line currently at the top of the screen X * and redisplay the screen. X */ X prepaint(position(TOP)); X} X X/* X * Jump to the end of the file. X * It is more convenient to paint the screen backward, X * from the end of the file toward the beginning. X */ X public void Xjump_forw() X{ X POSITION pos; X X if (ch_end_seek()) X { X error("Cannot seek to end of file"); X return; X } X lastmark(); X pos = ch_tell(); X clear(); X pos_clear(); X add_back_pos(pos); X back(sc_height - 1, pos, 0, 0); X} X X/* X * Jump to line n in the file. X */ X public void Xjump_back(n) X register int n; X{ X register int c; X int nlines; X X /* X * This is done the slow way, by starting at the beginning X * of the file and counting newlines. X */ X if (ch_seek((POSITION)0)) X { X /* X * Probably a pipe with beginning of file no longer buffered. X * If he wants to go to line 1, we do the best we can, X * by going to the first line which is still buffered. X */ X if (n <= 1 && ch_beg_seek() == 0) X jump_loc(ch_tell()); X error("Cannot get to beginning of file"); X return; X } X X /* X * Start counting lines. X */ X for (nlines = 1; nlines < n; nlines++) X { X while ((c = ch_forw_get()) != '\n') X if (c == EOF) X { X char message[40]; X sprintf(message, "File has only %d lines", X nlines-1); X error(message); X return; X } X } X X jump_loc(ch_tell()); X} X X/* X * Jump to a specified percentage into the file. X * This is a poor compensation for not being able to X * quickly jump to a specific line number. X */ X public void Xjump_percent(percent) X int percent; X{ X POSITION pos, len; X register int c; X X /* X * Determine the position in the file X * (the specified percentage of the file's length). X */ X if ((len = ch_length()) == NULL_POSITION) X { X error("Don't know length of file"); X return; X } X pos = (percent * len) / 100; X X /* X * Back up to the beginning of the line. X */ X if (ch_seek(pos) == 0) X { X while ((c = ch_back_get()) != '\n' && c != EOF) X ; X if (c == '\n') X (void) ch_forw_get(); X pos = ch_tell(); X } X jump_loc(pos); X} X X/* X * Jump to a specified position in the file. X */ X public void Xjump_loc(pos) X POSITION pos; X{ X register int nline; X POSITION tpos; X X /* X * See if the desired line is BEFORE the currently X * displayed screen. If so, see if it is close enough X * to scroll backwards to it. X * {{ This can be expensive if he has specified a very X * large back_scroll count. Perhaps we should put X * some sanity limit on the loop count here. }} X */ X tpos = position(TOP); X if (tpos != NULL_POSITION && pos < tpos) X { X int bs = get_back_scroll(); X for (nline = 1; nline <= bs; nline++) X { X tpos = back_line(tpos); X if (tpos == NULL_POSITION) X break; X if (tpos <= pos) X { X back(nline, position(TOP), 1, 0); X return; X } X } X } else if ((nline = onscreen(pos)) >= 0) X { X /* X * The line is currently displayed. X * Just scroll there. X */ X forw(nline, position(BOTTOM_PLUS_ONE), 1, 0); X return; X } X X /* X * Line is not on screen. X * Remember where we were; clear and paint the screen. X */ X if (ch_seek(pos)) X { X error("Cannot seek to that position"); X return; X } X lastmark(); X prepaint(pos); X} X X/* X * The table of marks. X * A mark is simply a position in the file. X */ X#define NMARKS (27) /* 26 for a-z plus one for quote */ X#define LASTMARK (NMARKS-1) /* For quote */ Xstatic POSITION marks[NMARKS]; X X/* X * Initialize the mark table to show no marks are set. X */ X public void Xinit_mark() X{ X int i; X X for (i = 0; i < NMARKS; i++) X marks[i] = NULL_POSITION; X} X X/* X * See if a mark letter is valid (between a and z). X */ X static int Xbadmark(c) X int c; X{ X if (c < 'a' || c > 'z') X { X error("Choose a letter between 'a' and 'z'"); X return (1); X } X return (0); X} X X/* X * Set a mark. X */ X public void Xsetmark(c) X int c; X{ X if (badmark(c)) X return; X marks[c-'a'] = position(TOP); X} X X public void Xlastmark() X{ X marks[LASTMARK] = position(TOP); X} X X/* X * Go to a previously set mark. X */ X public void Xgomark(c) X int c; X{ X POSITION pos; X X if (c == '\'') X pos = marks[LASTMARK]; X else if (badmark(c)) X return; X else X pos = marks[c-'a']; X X if (pos == NULL_POSITION) X error("mark not set"); X else X jump_loc(pos); X} X X/* X * Get the backwards scroll limit. X * Must call this function instead of just using the value of X * back_scroll, because the default case depends on sc_height and X * top_scroll, as well as back_scroll. X */ X public int Xget_back_scroll() X{ X if (back_scroll < 0) X return (sc_height - 1 - top_scroll); X return (back_scroll); X} X X/* X * Search for the n-th occurence of a specified pattern, X * either forward (direction == '/'), or backwards (direction == '?'). X */ X public void Xsearch(direction, pattern, n) X int direction; X char *pattern; X register int n; X{ X register int search_forward = (direction == '/'); X POSITION pos, linepos; X X#if RECOMP X char *re_comp(); X char *errmsg; X X /* X * (re_comp handles a null pattern internally, X * so there is no need to check for a null pattern here.) X */ X if ((errmsg = re_comp(pattern)) != NULL) X { X error(errmsg); X return; X } X#else X#if REGCMP X char *regcmp(); X static char *cpattern = NULL; X X if (pattern == NULL || *pattern == '\0') X { X /* X * A null pattern means use the previous pattern. X * The compiled previous pattern is in cpattern, so just use it. X */ X if (cpattern == NULL) X { X error("No previous regular expression"); X return; X } X } else X { X /* X * Otherwise compile the given pattern. X */ X char *s; X if ((s = regcmp(pattern, 0)) == NULL) X { X error("Invalid pattern"); X return; X } X if (cpattern != NULL) X free(cpattern); X cpattern = s; X } X#else X static char lpbuf[100]; X static char *last_pattern = NULL; X X if (pattern == NULL || *pattern == '\0') X { X /* X * Null pattern means use the previous pattern. X */ X if (last_pattern == NULL) X { X error("No previous regular expression"); X return; X } X pattern = last_pattern; X } else X { X strcpy(lpbuf, pattern); X last_pattern = lpbuf; X } X#endif X#endif X X /* X * Figure out where to start the search. X */ X X if (position(TOP) == NULL_POSITION) X { X /* X * Nothing is currently displayed. X * Start at the beginning of the file. X * (This case is mainly for first_cmd searches, X * for example, "+/xyz" on the command line.) X */ X pos = (POSITION)0; X } else if (!search_forward) X { X /* X * Backward search: start just before the top line X * displayed on the screen. X */ X pos = position(TOP); X } else if (top_search) X { X /* X * Forward search and "start from top". X * Start at the second line displayed on the screen. X */ X pos = position(TOP_PLUS_ONE); X } else X { X /* X * Forward search but don't "start from top". X * Start just after the bottom line displayed on the screen. X */ X pos = position(BOTTOM_PLUS_ONE); X } X X if (pos == NULL_POSITION) X { X /* X * Can't find anyplace to start searching from. X */ X error("Nothing to search"); X return; X } X X for (;;) X { X /* X * Get lines until we find a matching one or X * until we hit end-of-file (or beginning-of-file X * if we're going backwards). X */ X if (sigs) X /* X * A signal aborts the search. X */ X return; X X if (search_forward) X { X /* X * Read the next line, and save the X * starting position of that line in linepos. X */ X linepos = pos; X pos = forw_raw_line(pos); X } else X { X /* X * Read the previous line and save the X * starting position of that line in linepos. X */ X pos = back_raw_line(pos); X linepos = pos; X } X X if (pos == NULL_POSITION) X { X /* X * We hit EOF/BOF without a match. X */ X error("Pattern not found"); X return; X } X X /* X * Test the next line to see if we have a match. X * This is done in a variety of ways, depending X * on what pattern matching functions are available. X */ X#if REGCMP X if ( (regex(cpattern, line) != NULL) X#else X#if RECOMP X if ( (re_exec(line) == 1) X#else X if ( (match(pattern, line)) X#endif X#endif X && (--n <= 0) ) X /* X * Found the matching line. X */ X break; X } X X jump_loc(linepos); X} X X#if (!REGCMP) && (!RECOMP) X/* X * We have neither regcmp() nor re_comp(). X * We use this function to do simple pattern matching. X * It supports no metacharacters like *, etc. X */ X static int Xmatch(pattern, buf) X char *pattern, *buf; X{ X register char *pp, *lp; X X for ( ; *buf != '\0'; buf++) X { X for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) X if (*pp == '\0' || *lp == '\0') X break; X if (*pp == '\0') X return (1); X } X return (0); X} X#endif SHAR_EOF fi if test -f 'prompt.c' then echo shar: "will not over-write existing file 'prompt.c'" else sed 's/^X//' << \SHAR_EOF > 'prompt.c' X/* X * Prompting and other messages. X * There are three flavors of prompts, SHORT, MEDIUM and LONG, X * selected by the -m/-M options. X * A prompt is either a colon or a message composed of various X * pieces, such as the name of the file being viewed, the percentage X * into the file, etc. X */ X X#include "less.h" X#include "position.h" X Xextern int pr_type; Xextern int ispipe; Xextern int hit_eof; Xextern int new_file; Xextern int sc_width; Xextern char current_file[]; Xextern int ac; Xextern char **av; Xextern int curr_ac; X X/* X * Prototypes for the three flavors of prompts. X * These strings are expanded by pr_expand(). X */ Xchar *prproto[] = { X "fo", /* PR_SHORT */ X "foP", /* PR_MEDIUM */ X "Fobp" /* PR_LONG */ X}; X Xstatic char message[200]; Xstatic char *mp; X X static void Xsetmp() X{ X mp = message + strlen(message); X} X X/* X * Append the name of the current file (to the message buffer). X */ X static void Xap_filename() X{ X if (ispipe) X return; X strtcpy(mp, current_file, &message[sizeof(message)] - mp); X setmp(); X} X X/* X * Append the "file N of M" message. X */ X static void Xap_of() X{ X if (ac <= 1) X return; X sprintf(mp, " (file %d of %d)", curr_ac+1, ac); X setmp(); X} X X/* X * Append the byte offset into the current file. X */ X static void Xap_byte() X{ X POSITION pos, len; X X pos = position(BOTTOM_PLUS_ONE); X if (pos == NULL_POSITION) X pos = ch_length(); X if (pos != NULL_POSITION) X { X sprintf(mp, " byte %ld", (long)pos); X setmp(); X len = ch_length(); X if (len > 0) X { X sprintf(mp, "/%ld", (long)len); X setmp(); X } X } X} X X/* X * Append the percentage into the current file. X * If we cannot find the percentage and must_print is true, X * use the byte offset. X */ X static void Xap_percent(must_print) X{ X POSITION pos,len; X X pos = position(BOTTOM_PLUS_ONE); X len = ch_length(); X if (len > 0 && pos != NULL_POSITION) X { X sprintf(mp, " (%ld%%)", (100 * (long)pos) / len); X setmp(); X } else if (must_print) X ap_byte(); X} X X/* X * Append the end-of-file message. X */ X static void Xap_eof() X{ X strcpy(mp, " (END)"); X setmp(); X if (curr_ac + 1 < ac) X { X sprintf(mp, " - Next: %s", av[curr_ac+1]); X setmp(); X } X} X X/* X * Construct a message based on a prototype string. X */ X static char * Xpr_expand(proto, maxwidth) X char *proto; X int maxwidth; X{ X register char *p; X X mp = message; X X for (p = proto; *p != '\0'; p++) X { X if (maxwidth > 0 && mp >= message + maxwidth) X { X /* X * Truncate to the screen width. X * {{ This isn't very nice. }} X */ X mp = message + maxwidth; X break; X } X switch (*p) X { X case 'f': X if (new_file) X ap_filename(); X break; X case 'F': X ap_filename(); X break; X case 'o': X if (new_file) X ap_of(); X break; X case 'O': X ap_of(); X break; X case 'b': X ap_byte(); X break; X case 'p': X if (!hit_eof) X ap_percent(0); X break; X case 'P': X if (!hit_eof) X ap_percent(1); X break; X case '<': X while (*++p != '>') X { X if (*p == '\0') X { X p--; X break; X } X *mp++ = *p; X } X break; X default: X *mp++ = *p; X break; X } X } X if (hit_eof) X ap_eof(); X X new_file = 0; X if (mp == message) X return (NULL); X *mp = '\0'; X return (message); X} X X/* X * Return a message suitable for printing by the "=" command. X */ X public char * Xeq_message() X{ X return (pr_expand("FObp", 0)); X} X X/* X * Return a prompt. X * This depends on the prompt type (SHORT, MEDIUM, LONG), etc. X * If we can't come up with an appropriate prompt, return NULL X * and the caller will prompt with a colon. X */ X public char * Xpr_string() X{ X return (pr_expand(prproto[pr_type], sc_width-2)); X} SHAR_EOF fi if test -f 'screen.c' then echo shar: "will not over-write existing file 'screen.c'" else sed 's/^X//' << \SHAR_EOF > 'screen.c' X/* X * Routines which deal with the characteristics of the terminal. X * Uses termcap to be as terminal-independent as possible. X * X * {{ Someday this should be rewritten to use curses. }} X */ X X#include "less.h" X#if XENIX X#include X#include X#endif X X#if TERMIO X#include X#else X#include X#endif X X/* X * Strings passed to tputs() to do various terminal functions. X */ Xstatic char X *sc_pad, /* Pad string */ X *sc_home, /* Cursor home */ X *sc_addline, /* Add line, scroll down following lines */ X *sc_lower_left, /* Cursor to last line, first column */ X *sc_move, /* General cursor positioning */ X *sc_clear, /* Clear screen */ X *sc_eol_clear, /* Clear to end of line */ X *sc_s_in, /* Enter standout (highlighted) mode */ X *sc_s_out, /* Exit standout mode */ X *sc_u_in, /* Enter underline mode */ X *sc_u_out, /* Exit underline mode */ X *sc_b_in, /* Enter bold mode */ X *sc_b_out, /* Exit bold mode */ X *sc_visual_bell, /* Visual bell (flash screen) sequence */ X *sc_backspace, /* Backspace cursor */ X *sc_init, /* Startup terminal initialization */ X *sc_deinit; /* Exit terminal de-intialization */ Xstatic int dumb; Xstatic int hard; X Xpublic int auto_wrap; /* Terminal does \r\n when write past margin */ Xpublic int ignaw; /* Terminal ignores \n immediately after wrap */ Xpublic int erase_char, kill_char; /* The user's erase and line-kill chars */ Xpublic int sc_width, sc_height; /* Height & width of screen */ Xpublic int sc_window = -1; /* window size for forward and backward */ Xpublic int bo_width, be_width; /* Printing width of boldface sequences */ Xpublic int ul_width, ue_width; /* Printing width of underline sequences */ Xpublic int so_width, se_width; /* Printing width of standout sequences */ X X/* X * These two variables are sometimes defined in, X * and needed by, the termcap library. X * It may be necessary on some systems to declare them extern here. X */ X/*extern*/ short ospeed; /* Terminal output baud rate */ X/*extern*/ char PC; /* Pad character */ X Xextern int quiet; /* If VERY_QUIET, use visual bell for bell */ Xextern int know_dumb; /* Don't complain about a dumb terminal */ Xextern int back_scroll; Xchar *tgetstr(); Xchar *tgoto(); X X/* X * Change terminal to "raw mode", or restore to "normal" mode. X * "Raw mode" means X * 1. An outstanding read will complete on receipt of a single keystroke. X * 2. Input is not echoed. X * 3. On output, \n is mapped to \r\n. X * 4. \t is NOT expanded into spaces. X * 5. Signal-causing characters such as ctrl-C (interrupt), X * etc. are NOT disabled. X * It doesn't matter whether an input \n is mapped to \r, or vice versa. X */ X public void Xraw_mode(on) X int on; X{ X#if TERMIO X struct termio s; X static struct termio save_term; X X if (on) X { X /* X * Get terminal modes. X */ X ioctl(2, TCGETA, &s); X X /* X * Save modes and set certain variables dependent on modes. X */ X save_term = s; X ospeed = s.c_cflag & CBAUD; X erase_char = s.c_cc[VERASE]; X kill_char = s.c_cc[VKILL]; X X /* X * Set the modes to the way we want them. X */ X s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); X s.c_oflag |= (OPOST|ONLCR|TAB3); X s.c_oflag &= ~(OCRNL|ONOCR|ONLRET); X s.c_cc[VMIN] = 1; X s.c_cc[VTIME] = 0; X } else X { X /* X * Restore saved modes. X */ X s = save_term; X } X ioctl(2, TCSETAW, &s); X#else X struct sgttyb s; X static struct sgttyb save_term; X X if (on) X { X /* X * Get terminal modes. X */ X ioctl(2, TIOCGETP, &s); X X /* X * Save modes and set certain variables dependent on modes. X */ X save_term = s; X ospeed = s.sg_ospeed; X erase_char = s.sg_erase; X kill_char = s.sg_kill; X X /* X * Set the modes to the way we want them. X */ X s.sg_flags |= CBREAK; X s.sg_flags &= ~(ECHO|XTABS); X } else X { X /* X * Restore saved modes. X */ X s = save_term; X } X ioctl(2, TIOCSETN, &s); X#endif X} X X static void Xcannot(s) X char *s; X{ X char message[100]; X X if (know_dumb) X /* X * He knows he has a dumb terminal, so don't tell him. X */ X return; X X sprintf(message, "WARNING: terminal cannot \"%s\"", s); X error(message); X} X X/* X * Get terminal capabilities via termcap. X */ X public void Xget_term() X{ X char termbuf[1024]; X char *sp; X static char sbuf[150]; X X char *getenv(); X X /* X * Find out what kind of terminal this is. X */ X if (tgetent(termbuf, getenv("TERM")) <= 0) X dumb = 1; X X /* X * Get size of the screen. X */ X if (dumb || (sc_height = tgetnum("li")) < 0 || tgetflag("hc")) X { X /* Oh no, this is a hardcopy terminal. */ X hard = 1; X sc_height = 24; X } X /* X * This is terrible - the following if "knows" that it is being X * executed *after* command line and environment options have X * already been parsed. Should it be executed in the main program X * instead? X */ X if ((sc_window <= 0) || (sc_window >= sc_height)) X sc_window = sc_height-1; X if (dumb || (sc_width = tgetnum("co")) < 0) X sc_width = 80; X X auto_wrap = tgetflag("am"); X ignaw = tgetflag("xn"); X X /* X * Assumes termcap variable "sg" is the printing width of X * the standout sequence, the end standout sequence, X * the underline sequence, the end underline sequence, X * the boldface sequence, and the end boldface sequence. X */ X if ((so_width = tgetnum("sg")) < 0) X so_width = 0; X be_width = bo_width = ue_width = ul_width = se_width = so_width; X X /* X * Get various string-valued capabilities. X */ X sp = sbuf; X X sc_pad = (dumb) ? NULL : tgetstr("pc", &sp); X if (sc_pad != NULL) X PC = *sc_pad; X X sc_init = (dumb) ? NULL : tgetstr("ti", &sp); X if (sc_init == NULL) X sc_init = ""; X X sc_deinit= (dumb) ? NULL : tgetstr("te", &sp); X if (sc_deinit == NULL) X sc_deinit = ""; X X sc_eol_clear = (dumb) ? NULL : tgetstr("ce", &sp); X if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0') X { X cannot("clear to end of line"); X sc_eol_clear = ""; X } X X sc_clear = (dumb) ? NULL : tgetstr("cl", &sp); X if (hard || sc_clear == NULL || *sc_clear == '\0') X { X cannot("clear screen"); X sc_clear = "\n\n"; X } X X sc_move = (dumb) ? NULL : tgetstr("cm", &sp); X if (hard || sc_move == NULL || *sc_move == '\0') X { X /* X * This is not an error here, because we don't X * always need sc_move. X * We need it only if we don't have home or lower-left. X */ X sc_move = ""; X } X X sc_s_in = (dumb) ? NULL : tgetstr("so", &sp); X if (hard || sc_s_in == NULL) X sc_s_in = ""; X X sc_s_out = (dumb) ? NULL : tgetstr("se", &sp); X if (hard || sc_s_out == NULL) X sc_s_out = ""; X X sc_u_in = (dumb) ? NULL : tgetstr("us", &sp); X if (hard || sc_u_in == NULL) X sc_u_in = sc_s_in; X X sc_u_out = (dumb) ? NULL : tgetstr("ue", &sp); X if (hard || sc_u_out == NULL) X sc_u_out = sc_s_out; X X sc_b_in = (dumb) ? NULL : tgetstr("md", &sp); X if (hard || sc_b_in == NULL) X { X sc_b_in = sc_s_in; X sc_b_out = sc_s_out; X } else X { X sc_b_out = (dumb) ? NULL : tgetstr("me", &sp); X if (hard || sc_b_out == NULL) X sc_b_out = ""; X } X X sc_visual_bell = (dumb) ? NULL : tgetstr("vb", &sp); X if (hard || sc_visual_bell == NULL) X sc_visual_bell = ""; X X sc_home = (dumb) ? NULL : tgetstr("ho", &sp); X if (hard || sc_home == NULL || *sc_home == '\0') X { X if (*sc_move == '\0') X { X cannot("home cursor"); X /* X * This last resort for sc_home is supposed to X * be an up-arrow suggesting moving to the X * top of the "virtual screen". (The one in X * your imagination as you try to use this on X * a hard copy terminal.) X */ X sc_home = "|\b^"; X } else X { X /* X * No "home" string, X * but we can use "move(0,0)". X */ X strcpy(sp, tgoto(sc_move, 0, 0)); X sc_home = sp; X sp += strlen(sp) + 1; X } X } X X sc_lower_left = (dumb) ? NULL : tgetstr("ll", &sp); X if (hard || sc_lower_left == NULL || *sc_lower_left == '\0') X { X if (*sc_move == '\0') X { X cannot("move cursor to lower left of screen"); X sc_lower_left = "\r"; X } else X { X /* X * No "lower-left" string, X * but we can use "move(0,last-line)". X */ X strcpy(sp, tgoto(sc_move, 0, sc_height-1)); X sc_lower_left = sp; X sp += strlen(sp) + 1; X } X } X X /* X * To add a line at top of screen and scroll the display down, X * we use "al" (add line) or "sr" (scroll reverse). X */ X if (dumb) X sc_addline = NULL; X else if ((sc_addline = tgetstr("al", &sp)) == NULL || X *sc_addline == '\0') X sc_addline = tgetstr("sr", &sp); X X if (hard || sc_addline == NULL || *sc_addline == '\0') X { X cannot("scroll backwards"); X sc_addline = ""; X /* Force repaint on any backward movement */ X back_scroll = 0; X } X X if (dumb || tgetflag("bs")) X sc_backspace = "\b"; X else X { X sc_backspace = tgetstr("bc", &sp); X if (sc_backspace == NULL || *sc_backspace == '\0') X sc_backspace = "\b"; X } X} X X X/* X * Below are the functions which perform all the X * terminal-specific screen manipulation. X */ X X X/* X * Initialize terminal X */ X public void Xinit() X{ X tputs(sc_init, sc_height, putc); X} X X/* X * Deinitialize terminal X */ X public void Xdeinit() X{ X tputs(sc_deinit, sc_height, putc); X} X X/* X * Home cursor (move to upper left corner of screen). X */ X public void Xhome() X{ X tputs(sc_home, 1, putc); X} X X/* X * Add a blank line (called with cursor at home). X * Should scroll the display down. X */ X public void Xadd_line() X{ X tputs(sc_addline, sc_height, putc); X} X X/* X * Move cursor to lower left corner of screen. X */ X public void Xlower_left() X{ X tputs(sc_lower_left, 1, putc); X} X X/* X * Ring the terminal bell. X */ X public void Xbell() X{ X if (quiet == VERY_QUIET) X vbell(); X else X putc('\7'); X} X X/* X * Output the "visual bell", if there is one. X */ X public void Xvbell() X{ X if (*sc_visual_bell == '\0') X return; X tputs(sc_visual_bell, sc_height, putc); X} X X/* X * Clear the screen. X */ X public void Xclear() X{ X tputs(sc_clear, sc_height, putc); X} X X/* X * Clear from the cursor to the end of the cursor's line. X * {{ This must not move the cursor. }} X */ X public void Xclear_eol() X{ X tputs(sc_eol_clear, 1, putc); X} X X/* X * Begin "standout" (bold, underline, or whatever). X */ X public void Xso_enter() X{ X tputs(sc_s_in, 1, putc); X} X X/* X * End "standout". X */ X public void Xso_exit() X{ X tputs(sc_s_out, 1, putc); X} X X/* X * Begin "underline" (hopefully real underlining, X * otherwise whatever the terminal provides). X */ X public void Xul_enter() X{ X tputs(sc_u_in, 1, putc); X} X X/* X * End "underline". X */ X public void Xul_exit() X{ X tputs(sc_u_out, 1, putc); X} X X/* X * Begin "bold" X */ X public void Xbo_enter() X{ X tputs(sc_b_in, 1, putc); X} X X/* X * End "bold". X */ X public void Xbo_exit() X{ X tputs(sc_b_out, 1, putc); X} X X/* X * Erase the character to the left of the cursor X * and move the cursor left. X */ X public void Xbackspace() X{ X /* X * Try to erase the previous character by overstriking with a space. X */ X tputs(sc_backspace, 1, putc); X putc(' '); X tputs(sc_backspace, 1, putc); X} X X/* X * Output a plain backspace, without erasing the previous char. X */ X public void Xputbs() X{ X tputs(sc_backspace, 1, putc); X} SHAR_EOF fi if test -f 'signal.c' then echo shar: "will not over-write existing file 'signal.c'" else sed 's/^X//' << \SHAR_EOF > 'signal.c' X/* X * Routines dealing with signals. X * X * A signal usually merely causes a bit to be set in the "signals" word. X * At some convenient time, the mainline code checks to see if any X * signals need processing by calling psignal(). X * An exception is made if we are reading from the keyboard when the X * signal is received. Some operating systems will simply call the X * signal handler and NOT return from the read (with EINTR). X * To handle this case, we service the interrupt directly from X * the handler if we are reading from the keyboard. X */ X X#include "less.h" X#include X#include X X/* X * The type of signal handler functions. X * Usually int, although it should be void. X */ Xtypedef int HANDLER; X X/* X * "sigs" contains bits indicating signals which need to be processed. X */ Xpublic int sigs; X#define S_INTERRUPT 01 X#ifdef SIGTSTP X#define S_STOP 02 X#endif X Xextern int reading; Xextern char *first_cmd; Xextern jmp_buf main_loop; X X/* X * Interrupt signal handler. X */ X static HANDLER Xinterrupt() X{ X SIGNAL(SIGINT, interrupt); X sigs |= S_INTERRUPT; X if (reading) X psignals(); X} X X#ifdef SIGTSTP X/* X * "Stop" (^Z) signal handler. X */ X static HANDLER Xstop() X{ X SIGNAL(SIGTSTP, stop); X sigs |= S_STOP; X if (reading) X psignals(); X} X#endif X X/* X * Set up the signal handlers. X */ X public void Xinit_signals() X{ X (void) SIGNAL(SIGINT, interrupt); X#ifdef SIGTSTP X (void) SIGNAL(SIGTSTP, stop); X#endif X} X X/* X * Process any signals we have recieved. X * A received signal cause a bit to be set in "sigs". X */ X public void Xpsignals() X{ X register int tsignals; X X tsignals = sigs; X sigs = 0; X if (tsignals == 0) X return; X X dropout(); /* Discard any buffered output */ X X#ifdef SIGTSTP X if (tsignals & S_STOP) X { X /* X * Clean up the terminal. X */ X#ifdef SIGTTOU X SIGNAL(SIGTTOU, SIG_IGN); X#endif X lower_left(); X clear_eol(); X flush(); X raw_mode(0); X#ifdef SIGTTOU X SIGNAL(SIGTTOU, SIG_DFL); X#endif X SIGNAL(SIGTSTP, SIG_DFL); X#if SIGSETMASK X /* X * This system will not allow us to send a X * stop signal (SIGTSTP) to ourself X * while we are in the signal handler, like maybe now. X * (This can be the case if we are reading; see comment above.) X * So we ask the silly system for permission to do so. X */ X sigsetmask(0); X#endif X kill(getpid(), SIGTSTP); X /* X * ... Bye bye. ... X * Hopefully we'll be back later and resume here... X * Reset the terminal and arrange to repaint the X * screen when we get back to the main command loop. X */ X SIGNAL(SIGTSTP, stop); X raw_mode(1); X first_cmd = "r"; X longjmp(main_loop, 1); X } X#endif X if (tsignals & S_INTERRUPT) X { X bell(); X /* X * {{ You may wish to replace the bell() with X * error("Interrupt"); }} X */ X } X X longjmp(main_loop, 1); X} X X/* X * Pass the specified command to a shell to be executed. X * Like plain "system()", but handles resetting terminal modes, etc. X */ X public void Xlsystem(cmd) X char *cmd; X{ X int inp; X X /* X * Print the command which is to be executed, X * unless the command starts with a "-". X */ X if (cmd[0] == '-') X cmd++; X else X { X lower_left(); X clear_eol(); X puts("!"); X puts(cmd); X puts("\n"); X } X X /* X * De-initialize the terminal and take out of raw mode. X */ X deinit(); X flush(); X raw_mode(0); X X /* X * Restore signals to their defaults. X */ X SIGNAL(SIGINT, SIG_DFL); X#ifdef SIGTSTP X SIGNAL(SIGTSTP, SIG_DFL); X#endif X /* X * Force standard input to be the terminal, "/dev/tty". X */ X inp = dup(0); X close(0); X open("/dev/tty", 0); X X /* X * Pass the command to the system to be executed. X */ X system(cmd); X X /* X * Restore standard input, reset signals, raw mode, etc. X */ X close(0); X dup(inp); X close(inp); X X init_signals(); X raw_mode(1); X init(); X} X X/* X * Expand a filename, substituting any environment variables, etc. X * The implementation of this is necessarily very operating system X * dependent. This implementation is unabashedly only for Unix systems. X */ X#if GLOB X X#include X X char * Xglob(filename) X char *filename; X{ X FILE *f; X char *p; X int ch; X static char filebuf[128]; X static char ECHO[] = "echo "; X X strcpy(filebuf, ECHO); X strtcpy(filebuf+sizeof(ECHO)-1, filename, sizeof(filebuf)-sizeof(ECHO)); X if ((f = popen(filebuf, "r")) == NULL) X return (filename); X for (p = filebuf; p < &filebuf[sizeof(filebuf)-1]; p++) X { X if ((ch = getc(f)) == '\n' || ch == EOF) X break; X *p = ch; X } X *p = '\0'; X pclose(f); X return (filebuf); X} X X#else X X char * Xglob(filename) X char *filename; X{ X return (filename); X} X X#endif SHAR_EOF fi if test -f 'ttyin.c' then echo shar: "will not over-write existing file 'ttyin.c'" else sed 's/^X//' << \SHAR_EOF > 'ttyin.c' X/* X * Routines dealing with getting input from the keyboard (i.e. from the user). X */ X X#include "less.h" X X/* X * The boolean "reading" is set true or false according to whether X * we are currently reading from the keyboard. X * This information is used by the signal handling stuff in signal.c. X * {{ There are probably some race conditions here X * involving the variable "reading". }} X */ Xpublic int reading; X Xstatic int tty; X X/* X * Open keyboard for input. X * (Just use file descriptor 2.) X */ X public void Xopen_getc() X{ X tty = 2; X} X X/* X * Get a character from the keyboard. X */ X public int Xgetc() X{ X char c; X int result; X X reading = 1; X do X { X flush(); X result = read(tty, &c, 1); X } while (result != 1); X reading = 0; X return (c & 0177); X} SHAR_EOF fi if test -f 'version.c' then echo shar: "will not over-write existing file 'version.c'" else sed 's/^X//' << \SHAR_EOF > 'version.c' X/* X * less X * Copyright (c) 1984,1985 Mark Nudelman X * X * This program may be freely used and/or modified, X * with the following provisions: X * 1. This notice and the above copyright notice must remain intact. X * 2. Neither this program, nor any modification of it, X * may be sold for profit without written consent of the author. X * X * ----------------------------------------------------------------- X * X * This program is a paginator similar to "more", X * but allows you to move both forward and backward in the file. X * Commands are based on "more" and "vi". X * X * ----------------------- CHANGES --------------------------------- X * X * Allowed use on standard input 1/29/84 markn X * Added E, N, P commands 2/1/84 markn X * Added '=' command, 'stop' signal handling 4/17/84 markn X * Added line folding 4/20/84 markn X * v2: Fixed '=' command to use BOTTOM_PLUS_ONE, X * instead of TOP, added 'p' & 'v' commands 4/27/84 markn X * v3: Added -m and -t options, '-' command 5/3/84 markn X * v4: Added LESS environment variable 5/3/84 markn X * v5: New comments, fixed '-' command slightly 5/3/84 markn X * v6: Added -Q, visual bell 5/15/84 markn X * v7: Fixed jump_back(n) bug: n should count real X * lines, not folded lines. Also allow number X * on G command. 5/24/84 markn X * v8: Re-do -q and -Q commands 5/30/84 markn X * v9: Added "+" argument 9/25/84 markn X * v10: Fixed bug in -b argument processing 10/10/84 markn X * v11: Made error() ring bell if \n not entered. 10/18/84 markn X * ----------------------------------------------------------------- X * v12: Reorganized signal handling and made X * portable to 4.2bsd. 2/13/85 mark X * v13: Reword error message for '-' command. 2/16/85 mark X * v14: Added -bf and -bp variants of -b. 2/22/85 mark X * v15: Miscellaneous changes. 2/25/85 mark X * v16: Added -u flag for backspace processing. 3/13/85 mark X * v17: Added j and k commands, X * changed -t default. 4/13/85 mark X * v18: Rewrote signal handling code. 4/20/85 mark X * v19: Got rid of "verbose" eq_message(). 5/2/85 mark X * Made search() scroll in some cases. X * v20: Fixed screen.c ioctls for System V. 5/21/85 mark X * v21: Fixed some first_cmd bugs. 5/23/85 mark X * v22: Added support for no RECOMP nor REGCMP. 5/24/85 mark X * v23: Miscellanous changes and prettying up. 5/25/85 mark X * Posted to USENET. X * v24: Added ti,te terminal init & de-init 6/3/85 Mike Kersenbrock X * v25: Added -U flag, standout mode underlining. 6/8/85 mark X * v26: Added -M flag. 6/9/85 mark X * Use underline termcap (us) if it exists. X * v27: Renamed some variables to make unique in 6/15/85 mark X * 6 chars. Minor fix to -m. X * v28: Fixed right margin bug. 6/28/85 mark X * v29: Incorporated M.Rose's changes to signal.c 6/28/85 mark X * v30: Fixed stupid bug in argument processing. 6/29/85 mark X * v31: Added -p flag, changed repaint algorithm. 7/15/85 mark X * Added kludge for magic cookie terminals. X * v32: Added cat_file if output not a tty. 7/16/85 mark X * v33: Added -e flag and EDITOR. 7/23/85 mark X * v34: Added -s flag. 7/26/85 mark X * v35: Rewrote option handling; added option.c. 7/27/85 mark X * v36: Fixed -e flag to work if not last file. 7/29/85 mark X * v37: Added -x flag. 8/10/85 mark X * v38: Changed prompting; created prompt.c. 8/19/85 mark X * v39: (Not -p) does not initially clear screen. 8/24/85 mark X * v40: Added "skipping" indicator in forw(). 8/26/85 mark X * Posted to USENET. X * v41: ONLY_RETURN, control char commands, 9/17/85 mark X * faster search, other minor fixes. X * v42: Added ++ command line syntax; 9/25/85 mark X * ch_fsize for pipes. X * v43: Added -h flag, changed prim.c algorithms. 10/15/85 mark X * v44: Made END print in all cases of eof; 10/16/85 mark X * ignore SIGTTOU after receiving SIGTSTP. X * v45: Never print backspaces unless -u. 10/16/85 mark X * v46: Backwards scroll in jump_loc. 10/24/85 mark X * v47: Fixed bug in edit(): *first_cmd==0 10/30/85 mark X * v48: Use TIOCSETN instead of TIOCSETP. 11/16/85 mark X * Added marks (m and ' commands). X * Posted to USENET. X * ----------------------------------------------------------------- X * v49: Fixed bug: signal didn't clear mcc. 1/9/86 mark X * v50: Added ' (quote) to gomark. 1/15/86 mark X * v51: Added + cmd, fixed problem if first_cmd X * fails, made g cmd sort of "work" on pipes X * even if bof is no longer buffered. 1/16/86 mark X * v52: Made short files work better. 1/17/86 mark X * v53: Added -P option. 1/20/86 mark X * v54: Changed help to use HELPFILE. 1/20/86 mark X * v55: Messages work better if not tty output. 1/23/86 mark X * v56: Added -l option. 1/24/86 mark X * v57: Fixed -l to get confirmation before X * overwriting an existing file. 1/31/86 mark X * v58: Added filename globbing. 8/28/86 mark X * v59: Fixed some bugs with very long filenames. 9/15/86 mark X * v60: Incorporated changes from Leith (Casey) X * Leedom for boldface and -z option. 9/26/86 mark X * v61: Got rid of annoying repaints after ! cmd. 9/26/86 mark X * ----------------------------------------------------------------- X */ X Xchar version[] = "@(#) less version 61"; SHAR_EOF fi cd .. exit 0 # End of shell archive