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