keily@kodak.UUCP (dick keily) (01/14/87)
==================Edit thru here and run thru sh================= #! /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 the files: # line.c # option.c # output.c # position.c # prim.c # prompt.c # regerror.c # regsub.c # readme # This archive created: Wed Jan 14 11:27:54 1987 export PATH; PATH=/bin:$PATH if test -f 'line.c' then echo shar: will not over-write existing file "'line.c'" else cat << \SHAR_EOF > 'line.c' /* * 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_UNDERLINE mode. The complexity arises from the attempt * to deal with all cases, especially involving long lines with underlining. * There are still some cases which will break it. * * There are four states: * UL_NORMAL is the normal state (not in underline mode). * UL_YES means we are in underline mode. We expect to get * either a sequence like "_\bX" or "X\b_" to continue * underline mode, or just some ordinary characters * (no backspaces) to end underline mode. * UL_X means we are one character after UL_YES * (we have gotten the '_' in "_\bX" or the 'X' in "X\b_"). * UL_XB means we are one character after 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 UL_YES). */ static int ul_state; /* Currently in underline mode? */ #define UL_NORMAL 0 /* Not in underline mode */ #define UL_YES 1 /* In underline, need next char */ #define UL_X 2 /* In underline, got char, need \b */ #define UL_XB 3 /* In underline, got char & \b, need one more */ public char *line; /* Pointer to the current line. Usually points to linebuf. */ extern int bs_mode; extern int tabstop; extern int ul_width, ue_width; extern int sc_width, sc_height; /* * Rewind the line buffer. */ public void prewind() { line = curr = linebuf; ul_state = UL_NORMAL; column = 0; } /* * Append a character to the line buffer. * Expand tabs into spaces, handle underlining. * Returns 0 if ok, 1 if couldn't fit in buffer. */ #define NEW_COLUMN(newcol) if ((newcol) + ((ul_state)?ue_width:0) > sc_width) \ return (1); else column = (newcol) public int pappend(c) int c; { if (c == '\0') { /* * Terminate underline mode, if necessary. * Append a '\0' to the end of the line. */ switch (ul_state) { case UL_X: curr[0] = curr[-1]; curr[-1] = UE_CHAR; curr++; break; case UL_XB: case UL_YES: *curr++ = UE_CHAR; break; } ul_state = UL_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_UNDERLINE) { /* * Advance the state machine. */ switch (ul_state) { case UL_NORMAL: if (curr <= linebuf + 1 || curr[-1] != '\b') break; if (c != '_' && curr[-2] != '_') { curr -= 2; break; } /* * 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++; } /* Fall thru */ case UL_XB: /* * Termination of a sequnce "_\bX" or "X\b_". */ if (c == '_') c = curr[-2]; curr -= 2; ul_state = UL_YES; break; case UL_YES: if (column + ue_width + 1 >= sc_width) /* * We have just barely enough room to * exit underline mode. */ return (1); ul_state = UL_X; break; case UL_X: if (c == '\b') ul_state = 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 (ul_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++; ul_state = UL_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. */ 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 fi # end of overwriting check if test -f 'option.c' then echo shar: will not over-write existing file "'option.c'" else cat << \SHAR_EOF > 'option.c' /* * Process command line options. * Each option is a single letter which controls a program variable. * The options have defaults which may be changed via * the command line option, or toggled via the "-" command. */ #include "less.h" #define toupper(c) ((c)-'a'+'A') /* * Types of options. */ #define BOOL 01 /* Boolean option: 0 or 1 */ #define TRIPLE 02 /* Triple-valued option: 0, 1 or 2 */ #define NUMBER 04 /* Numeric option */ #define NO_TOGGLE 0100 /* Option cannot be toggled with "-" cmd */ /* * Variables controlled by command line options. */ public int p_nbufs, f_nbufs; /* Number of buffers. There are two values, one used for input from a pipe and the other for input from a file. */ public int clean_data; /* Can we assume the data is "clean"? (That is, free of nulls, etc) */ public int quiet; /* Should we suppress the audible bell? */ public int top_search; /* Should forward searches start at the top of the screen? (alternative is bottom) */ public int top_scroll; /* Repaint screen from top? (alternative is scroll from bottom) */ public int pr_type; /* Type of prompt (short, medium, long) */ public int bs_mode; /* How to process backspaces */ public int know_dumb; /* Don't complain about dumb terminals */ public int quit_at_eof; /* Quit after hitting end of file twice */ public int squeeze; /* Squeeze multiple blank lines into one */ public int tabstop; /* Tab settings */ public int back_scroll; /* Repaint screen on backwards movement */ public int twiddle; /* Display "~" for lines after EOF */ #ifdef MSDOS public int scrn_in_color; /* When using color monitor */ #endif extern int nbufs; extern char *first_cmd; extern char *every_first_cmd; #define DEF_F_NBUFS 5 /* Default for f_nbufs */ #define DEF_P_NBUFS 12 /* Default for p_nbufs */ static struct option { char oletter; /* The controlling letter (a-z) */ char otype; /* Type of the option */ int odefault; /* Default value */ int *ovar; /* Pointer to the associated variable */ char *odesc[3]; /* Description of each value */ } option[] = { { 'c', BOOL, 0, &clean_data, { "Don't assume data is clean", "Assume data is clean", NULL } }, { 'd', BOOL|NO_TOGGLE, 0, &know_dumb, { NULL, NULL, NULL} }, { 'e', BOOL, 0, &quit_at_eof, { "Don't quit at end-of-file", "Quit at end-of-file", NULL } }, { 'h', NUMBER, -1, &back_scroll, { "Backwards scroll limit is %d lines", NULL, NULL } }, { 'p', BOOL, 0, &top_scroll, { "Repaint by scrolling from bottom of screen", "Repaint by painting from top of screen", NULL } }, { 'x', NUMBER, 8, &tabstop, { "Tab stops every %d spaces", NULL, NULL } }, { 's', BOOL, 0, &squeeze, { "Don't squeeze multiple blank lines", "Squeeze multiple blank lines", NULL } }, { 't', BOOL, 1, &top_search, { "Forward search starts from bottom of screen", "Forward search starts from top of screen", NULL } }, { 'w', BOOL, 1, &twiddle, { "Display nothing for lines after end-of-file", "Display ~ for lines after end-of-file", NULL } }, { 'm', TRIPLE, 0, &pr_type, { "Prompt with a colon", "Prompt with a message", "Prompt with a verbose message" } }, { 'q', TRIPLE, 0, &quiet, { "Ring the bell for errors AND at eof/bof", "Ring the bell for errors but not at eof/bof", "Never ring the bell" } }, { 'u', TRIPLE, 0, &bs_mode, { "Underlined text displayed in underline mode", "All backspaces cause overstrike", "Backspaces print as ^H" } }, #ifdef MSDOS { 'C', BOOL|NO_TOGGLE, 0, &scrn_in_color, { NULL, NULL, NULL} }, #endif { '\0' } }; public char all_options[64]; /* List of all valid options */ /* * Initialize each option to its default value. */ public void init_option() { register struct option *o; register char *p; /* * First do special cases, not in option table. */ first_cmd = every_first_cmd = NULL; f_nbufs = DEF_F_NBUFS; /* -bf */ p_nbufs = DEF_P_NBUFS; /* -bp */ p = all_options; *p++ = 'b'; for (o = option; o->oletter != '\0'; o++) { /* * Set each variable to its default. * Also make a list of all options, in "all_options". */ *(o->ovar) = o->odefault; *p++ = o->oletter; if (o->otype & TRIPLE) *p++ = toupper(o->oletter); } *p = '\0'; } /* * Toggle command line flags from within the program. * Used by the "-" command. */ public void toggle_option(c) int c; { register struct option *o; char message[100]; char buf[5]; /* * First check for special cases not handled by the option table. */ switch (c) { case 'b': sprintf(message, "%d buffers", nbufs); error(message); return; } for (o = option; o->oletter != '\0'; o++) { if ((o->otype & BOOL) && (o->oletter == c) && (o->otype & NO_TOGGLE) == 0) { /* * Boolean option: * just toggle it. */ *(o->ovar) = ! *(o->ovar); error(o->odesc[*(o->ovar)]); return; } else if ((o->otype & TRIPLE) && (o->oletter == c) && (o->otype & NO_TOGGLE) == 0) { /* * Triple-valued option with lower case letter: * make it 1 unless already 1, then make it 0. */ *(o->ovar) = (*(o->ovar) == 1) ? 0 : 1; error(o->odesc[*(o->ovar)]); return; } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c) && (o->otype & NO_TOGGLE) == 0) { /* * Triple-valued option with upper case letter: * make it 2 unless already 2, then make it 0. */ *(o->ovar) = (*(o->ovar) == 2) ? 0 : 2; error(o->odesc[*(o->ovar)]); return; } else if ((o->otype & NUMBER) && (o->oletter == c) && (o->otype & NO_TOGGLE) == 0) { sprintf(message, o->odesc[0], *(o->ovar)); error(message); return; } } if (control_char(c)) sprintf(buf, "^%c", carat_char(c)); else sprintf(buf, "%c", c); sprintf(message, "\"-%s\": no such flag. Use one of \"%s\"", buf, all_options); error(message); } /* * Scan an argument (either from command line or from LESS environment * variable) and process it. */ public void scan_option(s) char *s; { register struct option *o; register int c; if (s == NULL) return; next: if (*s == '\0') return; switch (c = *s++) { case '-': case ' ': case '\t': goto next; case '+': if (*s == '+') every_first_cmd = ++s; first_cmd = s; return; case 'b': switch (*s) { case 'f': s++; f_nbufs = getnum(&s, 'b'); break; case 'p': s++; p_nbufs = getnum(&s, 'b'); break; default: f_nbufs = p_nbufs = getnum(&s, 'b'); break; } goto next; } for (o = option; o->oletter != '\0'; o++) { if ((o->otype & BOOL) && (o->oletter == c)) { *(o->ovar) = ! o->odefault; goto next; } else if ((o->otype & TRIPLE) && (o->oletter == c)) { *(o->ovar) = (o->odefault == 1) ? 0 : 1; goto next; } else if ((o->otype & TRIPLE) && (toupper(o->oletter) == c)) { *(o->ovar) = (o->odefault == 2) ? 0 : 2; goto next; } else if ((o->otype & NUMBER) && (o->oletter == c)) { *(o->ovar) = getnum(&s, c); goto next; } } printf("\"-%c\": invalid flag\n", c); exit(1); } /* * Translate a string into a number. * Like atoi(), but takes a pointer to a char *, and updates * the char * to point after the translated number. */ static int getnum(sp, c) char **sp; int c; { register char *s; register int n; s = *sp; if (*s < '0' || *s > '9') { printf("number is required after -%c\n", c); exit(1); } n = 0; while (*s >= '0' && *s <= '9') n = 10 * n + *s++ - '0'; *sp = s; return (n); } SHAR_EOF fi # end of overwriting check if test -f 'output.c' then echo shar: will not over-write existing file "'output.c'" else cat << \SHAR_EOF > 'output.c' /* * High level routines dealing with the output to the screen. */ #include "less.h" #ifdef MSDOS #include "scrn.h" #endif extern int sigs; extern int sc_width, sc_height; extern int ul_width, ue_width; extern int so_width, se_width; extern int tabstop; extern int twiddle; extern char *line; extern char *first_cmd; #ifdef MSDOS extern int scrn_in_color; #endif /* * Display the line which is in the line buffer. */ public void put_line() { register char *p; register int c; register int column; extern int auto_wrap, ignaw; if (sigs) /* * Don't output if a signal is pending. */ return; if (line == NULL) line = (twiddle) ? "~" : ""; column = 0; for (p = line; *p != '\0'; p++) { switch (c = *p) { case UL_CHAR: ul_enter(); column += ul_width; break; case UE_CHAR: ul_exit(); column += ue_width; break; case '\t': do { putc(' '); column++; } while ((column % tabstop) != 0); break; case '\b': putbs(); column--; break; default: if (c & 0200) { #ifndef MSDOS putc('^'); putc(c & 0177); column += 2; #endif } else { putc(c); column++; } } } if (column < sc_width || !auto_wrap || ignaw) putc('\n'); } /* * Is a given character a "control" character? * {{ ASCII DEPENDENT }} */ public int control_char(c) int c; { return (c < ' ' || c == '\177'); } /* * Return the printable character used to identify a control character * (printed after a carat; e.g. '\3' => "^C"). * {{ ASCII DEPENDENT }} */ public int carat_char(c) int c; { return ((c == '\177') ? '?' : (c | 0100)); } static char obuf[1024]; static char *ob = obuf; /* * Flush buffered output. */ public void flush() { #ifndef MSDOS write(1, obuf, ob-obuf); ob = obuf; #else int chr_cnt; int i; chr_cnt = ob-obuf; i = 0; do { if (scrn_in_color == 1) chr_put(*(obuf + i++), WHITE_ON_BLUE); else chr_put(*(obuf + i++), BW); --chr_cnt; } while (chr_cnt > 0); ob = obuf; #endif } /* * Discard buffered output. */ public void dropout() { ob = obuf; } /* * Output a character. */ public void putc(c) int c; { if (ob >= &obuf[sizeof(obuf)]) flush(); *ob++ = c; #ifdef MSDOS flush(); #endif } /* * Output a string. */ public void puts(s) register char *s; { #ifndef MSDOS while (*s != '\0') putc(*s++); #else str_put(s, WHITE_ON_RED); #endif } /* * Output a message in the lower left corner of the screen * and wait for carriage return. */ static char return_to_continue[] = " (press RETURN)"; public void error(s) char *s; { register int c; static char buf[2]; lower_left(); clear_eol(); so_enter(); #ifndef MSDOS puts(s); puts(return_to_continue); #else str_put(s, WHITE_ON_RED); str_put(return_to_continue, WHITE_ON_RED); #endif so_exit(); #if ONLY_RETURN while ((c = getc()) != '\n' && c != '\r') bell(); #else c = getc(); if (c != '\n' && c != '\r' && c != ' ') { buf[0] = c; first_cmd = buf; } #endif if (strlen(s) > sc_width) repaint(); } public int error_width() { /* * Don't use the last position, because some terminals * will scroll if you write in the last char of the last line. */ return (sc_width - (sizeof(return_to_continue) + so_width + se_width + 1)); } SHAR_EOF fi # end of overwriting check if test -f 'position.c' then echo shar: will not over-write existing file "'position.c'" else cat << \SHAR_EOF > 'position.c' /* * Routines dealing with the "position" table. * This is a table which tells the position (in the input file) of the * first char on each currently displayed line. * * {{ The position table is scrolled by moving all the entries. * Would be better to have a circular table * and just change a couple of pointers. }} */ #include "less.h" #include "position.h" #define NPOS 100 /* {{ sc_height must be less than NPOS }} */ static POSITION table[NPOS]; /* The position table */ extern int sc_width, sc_height; /* * Return the position of one of: * the top (first) line on the screen * the second line on the screen * the bottom line on the screen * the line after the bottom line on the screen */ public POSITION position(where) int where; { switch (where) { case BOTTOM: where = sc_height - 2; break; case BOTTOM_PLUS_ONE: where = sc_height - 1; break; } return (table[where]); } /* * Add a new file position to the bottom of the position table. */ public void add_forw_pos(pos) POSITION pos; { register int i; /* * Scroll the position table up. */ for (i = 1; i < sc_height; i++) table[i-1] = table[i]; table[sc_height - 1] = pos; } /* * Add a new file position to the top of the position table. */ public void add_back_pos(pos) POSITION pos; { register int i; /* * Scroll the position table down. */ for (i = sc_height - 1; i > 0; i--) table[i] = table[i-1]; table[0] = pos; } /* * Initialize the position table, done whenever we clear the screen. */ public void pos_clear() { register int i; for (i = 0; i < sc_height; i++) table[i] = NULL_POSITION; } /* * See if the byte at a specified position is currently on the screen. * Check the position table to see if the position falls within its range. * Return the position table entry if found, -1 if not. */ public int onscreen(pos) POSITION pos; { register int i; if (pos < table[0]) return (-1); for (i = 1; i < sc_height; i++) if (pos < table[i]) return (i-1); return (-1); } SHAR_EOF fi # end of overwriting check if test -f 'prim.c' then echo shar: will not over-write existing file "'prim.c'" else cat << \SHAR_EOF > 'prim.c' /* * Primitives for displaying the file on the screen. */ #include "less.h" #include "position.h" public int hit_eof; /* Keeps track of how many times we hit end of file */ extern int quiet; extern int top_search; extern int top_scroll; extern int back_scroll; extern int sc_width, sc_height; extern int sigs; extern char *line; extern char *first_cmd; /* * Sound the bell to indicate he is trying to move past end of file. */ static void eof_bell() { if (quiet == NOT_QUIET) bell(); else vbell(); } /* * Check to see if the end of file is currently "displayed". */ static void eof_check() { POSITION pos; /* * If the bottom line is empty, we are at EOF. * If the bottom line ends at the file length, * we must be just at EOF. */ pos = position(BOTTOM_PLUS_ONE); if (pos == NULL_POSITION || pos == ch_length()) hit_eof++; } /* * Display n lines, scrolling forward, * starting at position pos in the input file. * "force" means display the n lines even if we hit end of file. * "only_last" means display only the last screenful if n > screen size. */ static void forw(n, pos, force, only_last) register int n; POSITION pos; int force; int only_last; { int eof = 0; int nlines = 0; int repaint_flag; /* * repaint_flag tells us not to display anything till the end, * then just repaint the entire screen. */ repaint_flag = (only_last && n > sc_height-1); if (!repaint_flag) { if (top_scroll && n >= sc_height - 1) { /* * Start a new screen. * {{ This is not really desirable if we happen * to hit eof in the middle of this screen, * but we don't know if that will happen now. }} */ clear(); home(); force = 1; } else { lower_left(); clear_eol(); } if (pos != position(BOTTOM_PLUS_ONE)) { /* * This is not contiguous with what is * currently displayed. Clear the screen image * (position table) and start a new screen. */ pos_clear(); add_forw_pos(pos); force = 1; if (top_scroll) { clear(); home(); } else { puts("...skipping...\n"); } } } while (--n >= 0) { /* * Read the next line of input. */ pos = forw_line(pos); if (pos == NULL_POSITION) { /* * End of file: stop here unless the top line * is still empty, or "force" is true. */ eof = 1; if (!force && position(TOP) != NULL_POSITION) break; line = NULL; } /* * Add the position of the next line to the position table. * Display the current line on the screen. */ add_forw_pos(pos); nlines++; if (!repaint_flag) put_line(); } if (eof) hit_eof++; else eof_check(); if (nlines == 0) eof_bell(); else if (repaint_flag) repaint(); } /* * Display n lines, scrolling backward. */ static void back(n, pos, force, only_last) register int n; POSITION pos; int force; int only_last; { int nlines = 0; int repaint_flag; repaint_flag = (n > back_scroll || (only_last && n > sc_height-1)); hit_eof = 0; while (--n >= 0) { /* * Get the previous line of input. */ pos = back_line(pos); if (pos == NULL_POSITION) { /* * Beginning of file: stop here unless "force" is true. */ if (!force) break; line = NULL; } /* * Add the position of the previous line to the position table. * Display the line on the screen. */ add_back_pos(pos); nlines++; if (!repaint_flag) { home(); add_line(); put_line(); } } eof_check(); if (nlines == 0) eof_bell(); else if (repaint_flag) repaint(); } /* * Display n more lines, forward. * Start just after the line currently displayed at the bottom of the screen. */ public void forward(n, only_last) int n; int only_last; { POSITION pos; pos = position(BOTTOM_PLUS_ONE); if (pos == NULL_POSITION) { eof_bell(); hit_eof++; return; } forw(n, pos, 0, only_last); } /* * Display n more lines, backward. * Start just before the line currently displayed at the top of the screen. */ public void backward(n, only_last) int n; int only_last; { POSITION pos; pos = position(TOP); if (pos == NULL_POSITION) { /* * This will almost never happen, * because the top line is almost never empty. */ eof_bell(); return; } back(n, pos, 0, only_last); } /* * Repaint the screen, starting from a specified position. */ static void prepaint(pos) POSITION pos; { hit_eof = 0; forw(sc_height-1, pos, 0, 0); } /* * Repaint the screen. */ public void repaint() { /* * Start at the line currently at the top of the screen * and redisplay the screen. */ prepaint(position(TOP)); } /* * Jump to the end of the file. * It is more convenient to paint the screen backward, * from the end of the file toward the beginning. */ public void jump_forw() { POSITION pos; if (ch_end_seek()) { error("Cannot seek to end of file"); return; } pos = ch_tell(); clear(); pos_clear(); add_back_pos(pos); back(sc_height - 1, pos, 0, 0); } /* * Jump to line n in the file. */ public void jump_back(n) register int n; { register int c; /* * This is done the slow way, by starting at the beginning * of the file and counting newlines. */ if (ch_seek((POSITION)0)) { /* * Probably a pipe with beginning of file no longer buffered. */ error("Cannot get to beginning of file"); return; } /* * Start counting lines. */ while (--n > 0) { while ((c = ch_forw_get()) != '\n') if (c == EOF) { error("File is not that long"); /* {{ Maybe tell him how long it is? }} */ return; } } /* * Finally found the place to start. * Clear and redisplay the screen from there. * * {{ We *could* figure out if the new position is * close enough to just scroll there without clearing * the screen, but it's not worth it. }} */ prepaint(ch_tell()); } /* * Jump to a specified percentage into the file. * This is a poor compensation for not being able to * quickly jump to a specific line number. */ public void jump_percent(percent) int percent; { POSITION pos, len; /* * Determine the position in the file * (the specified percentage of the file's length). */ if ((len = ch_length()) == NULL_POSITION) { error("Don't know length of file"); return; } pos = (percent * len) / 100; jump_loc(pos); } public void jump_loc(pos) POSITION pos; { register int c; register int nline; POSITION tpos; /* * See if the desired line is BEFORE the currently * displayed screen. If so, see if it is close enough * to scroll backwards to it. */ tpos = position(TOP); if (pos < tpos) { for (nline = 1; nline <= back_scroll; nline++) { tpos = back_line(tpos); if (tpos == NULL_POSITION || tpos <= pos) { back(nline, position(TOP), 1, 0); return; } } } else if ((nline = onscreen(pos)) >= 0) { /* * The line is currently displayed. * Just scroll there. */ forw(nline, position(BOTTOM_PLUS_ONE), 1, 0); return; } /* * Line is not on screen. * Back up to the beginning of the current line. */ if (ch_seek(pos)) { error("Cannot seek to that position"); return; } while ((c = ch_back_get()) != '\n' && c != EOF) ; if (c == '\n') (void) ch_forw_get(); /* * Clear and paint the screen. */ prepaint(ch_tell()); } /* * The table of marks. * A mark is simply a position in the file. */ static POSITION marks[26]; /* * Initialize the mark table to show no marks are set. */ public void init_mark() { int i; for (i = 0; i < 26; i++) marks[i] = NULL_POSITION; } /* * See if a mark letter is valid (between a and z). */ static int badmark(c) int c; { if (c < 'a' || c > 'z') { error("Choose a letter between 'a' and 'z'"); return (1); } return (0); } /* * Set a mark. */ public void setmark(c) int c; { if (badmark(c)) return; marks[c-'a'] = position(TOP); } /* * Go to a previously set mark. */ public void gomark(c) int c; { POSITION pos; if (badmark(c)) return; if ((pos = marks[c-'a']) == NULL_POSITION) error("mark not set"); else jump_loc(pos); } /* * Search for the n-th occurence of a specified pattern, * either forward (direction == '/'), or backwards (direction == '?'). */ public void search(direction, pattern, n) int direction; char *pattern; register int n; { register int search_forward = (direction == '/'); POSITION pos, linepos; #if RECOMP char *re_comp(); char *errmsg; /* * (re_comp handles a null pattern internally, * so there is no need to check for a null pattern here.) */ if ((errmsg = re_comp(pattern)) != NULL) { error(errmsg); return; } #else #if REGCMP | REGEXP #if REGEXP /* Use Harry Spencer's regexpression source */ char *regcomp(); #else char *regcmp(); #endif static char *cpattern = NULL; if (pattern == NULL || *pattern == '\0') { /* * A null pattern means use the previous pattern. * The compiled previous pattern is in cpattern, so just use it. */ if (cpattern == NULL) { error("No previous regular expression"); return; } } else { /* * Otherwise compile the given pattern. */ char *s; #if REGEXP if ((s = regcomp(pattern, 0)) == NULL) #else if ((s = regcmp(pattern, 0)) == NULL) #endif { error("Invalid pattern"); return; } if (cpattern != NULL) free(cpattern); cpattern = s; } #else static char lpbuf[100]; static char *last_pattern = NULL; if (pattern == NULL || *pattern == '\0') { /* * Null pattern means use the previous pattern. */ if (last_pattern == NULL) { error("No previous regular expression"); return; } pattern = last_pattern; } else { strcpy(lpbuf, pattern); last_pattern = lpbuf; } #endif #endif /* * Figure out where to start the search. */ if (position(TOP) == NULL_POSITION) { /* * Nothing is currently displayed. * Start at the beginning of the file. * (This case is mainly for first_cmd searches, * for example, "+/xyz" on the command line.) */ pos = (POSITION)0; } else if (!search_forward) { /* * Backward search: start just before the top line * displayed on the screen. */ pos = position(TOP); } else if (top_search) { /* * Forward search and "start from top". * Start at the second line displayed on the screen. */ pos = position(TOP_PLUS_ONE); } else { /* * Forward search but don't "start from top". * Start just after the bottom line displayed on the screen. */ pos = position(BOTTOM_PLUS_ONE); } if (pos == NULL_POSITION) { /* * Can't find anyplace to start searching from. */ error("Nothing to search"); return; } for (;;) { /* * Get lines until we find a matching one or * until we hit end-of-file (or beginning-of-file * if we're going backwards). */ if (sigs) /* * A signal aborts the search. */ return; if (search_forward) { /* * Read the next line, and save the * starting position of that line in linepos. */ linepos = pos; pos = forw_raw_line(pos); } else { /* * Read the previous line and save the * starting position of that line in linepos. */ pos = back_raw_line(pos); linepos = pos; } if (pos == NULL_POSITION) { /* * We hit EOF/BOF without a match. */ error("Pattern not found"); return; } /* * Test the next line to see if we have a match. * This is done in a variety of ways, depending * on what pattern matching functions are available. */ #if REGCMP | REGEXP #if REGEXP if ( (regexec(cpattern, line) != NULL) #else if ( (regex(cpattern, line) != NULL) #endif #else #if RECOMP if ( (re_exec(line) == 1) #else if ( (match(pattern, line)) #endif #endif && (--n <= 0) ) /* * Found the matching line. */ break; } jump_loc(linepos); } #if (!REGCMP) && (!RECOMP) /* * We have neither regcmp() nor re_comp(). * We use this function to do simple pattern matching. * It supports no metacharacters like *, etc. */ static int match(pattern, buf) char *pattern, *buf; { register char *pp, *lp; for ( ; *buf != '\0'; buf++) { for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) if (*pp == '\0' || *lp == '\0') break; if (*pp == '\0') return (1); } return (0); } #endif SHAR_EOF fi # end of overwriting check if test -f 'prompt.c' then echo shar: will not over-write existing file "'prompt.c'" else cat << \SHAR_EOF > 'prompt.c' /* * 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; static char message[500]; /* * Append the name of the current file (to the message buffer). */ static void ap_filename() { if (!ispipe) sprintf(message + strlen(message), "%s", current_file); } /* * Append the "file N of M" message. */ static void ap_of() { if (ac > 1) sprintf(message + strlen(message), " (file %d of %d)", curr_ac+1, ac); } /* * Append the byte offset into the current file. */ static void ap_byte() { POSITION pos, len; pos = position(BOTTOM_PLUS_ONE); if (pos != NULL_POSITION) { sprintf(message + strlen(message), " byte %ld", pos); len = ch_length(); if (len > 0) sprintf(message + strlen(message), "/%ld", len); } } /* * Append the percentage into the current file. * If we cannot find the percentage and must_print is true, * the 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(message + strlen(message), " (%ld%%)", (100 * pos) / len); else if (must_print) ap_byte(); } /* * Append the end-of-file message. */ static void ap_eof() { strcat(message, " END"); if (curr_ac + 1 < ac) sprintf(message + strlen(message), " - Next: %s", av[curr_ac+1]); } /* * Return a message suitable for printing by the "=" command. */ public char * eq_message() { message[0] = '\0'; ap_filename(); ap_of(); ap_byte(); ap_percent(0); /* * Truncate to the screen width. * {{ This isn't very nice. }} */ message[error_width()] = '\0'; return (message); } /* * 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() { message[0] = '\0'; switch (pr_type) { case PR_SHORT: if (new_file) { ap_filename(); ap_of(); } if (hit_eof) ap_eof(); break; case PR_MEDIUM: if (new_file) { ap_filename(); ap_of(); } if (hit_eof) ap_eof(); else ap_percent(1); break; case PR_LONG: ap_filename(); if (new_file) ap_of(); ap_byte(); if (hit_eof) ap_eof(); else ap_percent(0); break; } new_file = 0; if (message[0] == '\0') return (NULL); /* * Truncate to the screen width. * {{ This isn't very nice. }} */ message[sc_width-2] = '\0'; return (message); } SHAR_EOF fi # end of overwriting check if test -f 'regerror.c' then echo shar: will not over-write existing file "'regerror.c'" else cat << \SHAR_EOF > 'regerror.c' #include <stdio.h> void regerror(s) char *s; { #ifndef DOSPORT #ifdef ERRAVAIL error("regexp: %s", s); #else fprintf(stderr, "regexp(3): %s", s); exit(1); #endif /* NOTREACHED */ #endif /* ifdef'd out for less's sake when reporting error inside less */ } SHAR_EOF fi # end of overwriting check if test -f 'regsub.c' then echo shar: will not over-write existing file "'regsub.c'" else cat << \SHAR_EOF > 'regsub.c' /* * regsub * * Copyright (c) 1986 by University of Toronto. * Written by Henry Spencer. Not derived from licensed software. * * Permission is granted to anyone to use this software for any * purpose on any computer system, and to redistribute it freely, * subject to the following restrictions: * * 1. The author is not responsible for the consequences of use of * this software, no matter how awful, even if they arise * from defects in it. * * 2. The origin of this software must not be misrepresented, either * by explicit claim or by omission. * * 3. Altered versions must be plainly marked as such, and must not * be misrepresented as being the original software. */ #include <stdio.h> #include "regexp.h" #include "regmagic.h" #ifndef CHARBITS #define UCHARAT(p) ((int)*(unsigned char *)(p)) #else #define UCHARAT(p) ((int)*(p)&CHARBITS) #endif /* - regsub - perform substitutions after a regexp match */ void regsub(prog, source, dest) regexp *prog; char *source; char *dest; { register char *src; register char *dst; register char c; register int no; register int len; extern char *strncpy(); if (prog == NULL || source == NULL || dest == NULL) { regerror("NULL parm to regsub"); return; } if (UCHARAT(prog->program) != MAGIC) { regerror("damaged regexp fed to regsub"); return; } src = source; dst = dest; while ((c = *src++) != '\0') { if (c == '&') no = 0; else if (c == '\\' && '0' <= *src && *src <= '9') no = *src++ - '0'; else no = -1; if (no < 0) { /* Ordinary character. */ if (c == '\\' && (*src == '\\' || *src == '&')) c = *src++; *dst++ = c; } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { len = prog->endp[no] - prog->startp[no]; (void) strncpy(dst, prog->startp[no], len); dst += len; if (len != 0 && *(dst-1) == '\0') { /*strncpy hit NUL. */ regerror("damaged match string"); return; } } } *dst++ = '\0'; } SHAR_EOF fi # end of overwriting check if test -f 'readme' then echo shar: will not over-write existing file "'readme'" else cat << \SHAR_EOF > 'readme' Port of less to msdos: Less is a program similar to the Unix more command but is much more useful. Read less.man for a good description of less and what it can do for you. The files included here will result in a full version of less for use on msdos. This version of less has been tested on all the pc systems that I could get my hands on. This includes monochrome, graphics, (that includes systems with graphics cards and mono monitors), and color graphics. The following must be done to have less.exe work in its full glory: 1. Nothing if you don't want color when using a color monitor. To get the color use the set command such as: set less=C (Case sensitive; c is another option) Do this prior to calling less. A command line option less -C <filename ...> will also get you color. NOTE: Only use the color option when using a color monitor. All other configurations need no special action. There are other less settings that can be used using the set command. Read less.man. The one I use is less=mC on an AT with enhanced monitor and ega card. Read on for the v command. 2. Less also has a v command that lets you can an editor from within less to edit the file that you are viewing at the time. The default is memacs which was taken from the Unix mod.sources. If you don't have it use the dos set command as follows to let less know which it is: set editor=your_editor 4. It is best to include the set commands in your autoexec.bat which will be executed when you boot up your system. 5. Less is a very useful utility. Read less.man and when in less the h command will put a description of the commands on the screen for you. 6. This version of less is named less13.exe for distribution. It replaces and other version that you have obtained from my posting of the executable. Accept no substitutes. When I get the time I will incorporate the changes to get up the distributed Unix version 61. If you post this to any bulletin board name any files associated with it with the version 13.(referring to the MSDOS control) 7. Less version 1.3 uses the system function but looks to see if you are using the swchar program that allows you to use the / rather than the \ character. The swchar state is changed before the system call if you are and then resets it so that it all becomes transparent to the user. 8. The source code for less is being posted to Net.sources. It is all ifdef'd so that it should compile on Unix systems also as is. 9. Version 1.3 corrects the following: The latest known to me fixes have been incorporated in the regexpression routines written by Henry Spencer and posted on the Unix news net. Dick Keily Eastman Kodak Company work: (716)477-1001 unix: ...!seismo!rochester!kodak!keily SHAR_EOF fi # end of overwriting check # End of shell archive exit 0