Path: utzoo!utgpu!cs.utexas.edu!usc!apple!amdcad!pyramid!ctnews!unix386!mark From: mark@unix386.Convergent.COM (Mark Nudelman) Newsgroups: alt.sources Subject: less (part 5 of 6) Message-ID: <6425@unix386.Convergent.COM> Date: 6 Mar 91 00:14:29 GMT Organization: Unisys/Convergent, San Jose, CA Lines: 2763 #! /bin/sh # This is a shell archive. # Remove anything before this line, then unpack it # by saving it into a file and typing "sh file". echo shar: Extracting \"charset.c\" sed "s/^X//" >'charset.c' <<'END_OF_FILE' X/* X * Functions to define the character set X * and do things specific to the character set. X */ X X#include "less.h" X X/* X * Predefined character sets, X * selected by the LESSCHARSET environment variable. X */ Xstruct charset { X char *name; X char *desc; X} charsets[] = { X { "ascii", "8bcccbcc18b95.b" }, X { "latin1", "8bcccbcc18b95.33b." }, X { NULL } X}; X X#define IS_BINARY_CHAR 01 X#define IS_CONTROL_CHAR 02 X Xstatic char chardef[256]; Xstatic char *binfmt = "\\%o"; X Xextern char *getenv(); X X/* X * Define a charset, given a description string. X * The string consists of 256 letters, X * one for each character in the charset. X * If the string is shorter than 256 letters, missing letters X * are taken to be identical to the last one. X * A decimal number followed by a letter is taken to be a X * repetition of the letter. X * X * Each letter is one of: X * . normal character X * b binary character X * c control character X */ X static void Xichardef(s) X char *s; X{ X register char *cp; X register int n; X register char v; X X n = 0; X cp = chardef; X while (*s != '\0') X { X switch (*s++) X { X case '.': X v = 0; X break; X case 'c': X v = IS_CONTROL_CHAR; X break; X case 'b': X v = IS_BINARY_CHAR|IS_CONTROL_CHAR; X break; X X case '0': case '1': case '2': case '3': case '4': X case '5': case '6': case '7': case '8': case '9': X n = (10 * n) + (s[-1] - '0'); X continue; X X default: X error("invalid chardef", NULL_PARG); X quit(1); X /*NOTREACHED*/ X } X X do X { X if (cp >= chardef + sizeof(chardef)) X { X error("chardef longer than 256", NULL_PARG); X quit(1); X /*NOTREACHED*/ X } X *cp++ = v; X } while (--n > 0); X n = 0; X } X X while (cp < chardef + sizeof(chardef)) X *cp++ = v; X} X X/* X * Define a charset, given a charset name. X * The valid charset names are listed in the "charsets" array. X */ X static int Xicharset(name) X register char *name; X{ X register struct charset *p; X X if (name == NULL || *name == '\0') X return (0); X X for (p = charsets; p->name != NULL; p++) X { X if (strcmp(name, p->name) == 0) X { X ichardef(p->desc); X return (1); X } X } X X error("invalid charset name", NULL_PARG); X quit(1); X /*NOTREACHED*/ X} X X/* X * Initialize charset data structures. X */ X public void Xinit_charset() X{ X register char *s; X X /* X * Try environment variable LESSCHARSET. X * If LESSCHARSET is not set, try LESSCHARDEF. X * If LESSCHARDEF is not set, default to "ascii" charset. X */ X s = getenv("LESSCHARSET"); X if (icharset(s)) X return; X X s = getenv("LESSCHARDEF"); X if (s != NULL && *s != '\0') X { X ichardef(s); X return; X } X X (void) icharset("ascii"); X X s = getenv("LESSBINFMT"); X if (s != NULL && *s != '\0') X binfmt = s; X} X X/* X * Is a given character a "binary" character? X */ X public int Xbinary_char(c) X int c; X{ X return (chardef[c] & IS_BINARY_CHAR); X} X X/* X * Is a given character a "control" character? X */ X public int Xcontrol_char(c) X int c; X{ X return (chardef[c] & IS_CONTROL_CHAR); X} X X/* X * Return the printable form of a character. X * For example, in the "ascii" charset '\3' is printed as "^C". X */ X public char * Xprchar(c) X int c; X{ X static char buf[8]; X X if (!control_char(c)) X sprintf(buf, "%c", c); X else if (!control_char(c ^ 0100)) X sprintf(buf, "^%c", c ^ 0100); X else X sprintf(buf, binfmt, c); X return (buf); X} END_OF_FILE echo shar: Extracting \"filename.c\" sed "s/^X//" >'filename.c' <<'END_OF_FILE' X/* X * Routines to mess around with filenames (and files). X * Much of this is very OS dependent. X */ X X#include X#include "less.h" X Xextern char *getenv(); X Xextern int force_open; Xextern IFILE curr_ifile; Xextern IFILE old_ifile; X X/* X * Return the full pathname of the given file in the "home directory". X */ X public char * Xhomefile(filename) X char *filename; X{ X register char *pathname; X register char *homedir; X X homedir = getenv("HOME"); X#if __MSDOS__ X /* X * Most MSDOS users do not have $HOME defined, X * so if no $HOME then look for "_less" anywhere X * on search path (always begins at current directory). X */ X if (homedir == NULL) X { X extern char *searchpath(); X pathname = searchpath(filename); X if (pathname == NULL) X return (NULL); X pathname = save(pathname); X } else X { X pathname = (char *) calloc(strlen(homedir)+strlen(filename)+2, X sizeof(char)); X if (pathname == NULL) X return (NULL); X sprintf(pathname, "%s\\%s", homedir, filename); X } X#else X if (homedir == NULL) X return (NULL); X pathname = (char *) calloc(strlen(homedir)+strlen(filename)+2, X sizeof(char)); X if (pathname == NULL) X return (NULL); X sprintf(pathname, "%s/%s", homedir, filename); X#endif X return (pathname); X} X X/* X * Find out where the help file is. X */ X public char * Xfind_helpfile() X{ X#if __MSDOS__ X extern char *searchpath(); X X /* X * Look in current directory. X */ X if (access(HELPFILE,0) == 0) X return (HELPFILE); X /* X * Find the basename of HELPFILE, X * and look for it in each directory in the search path. X */ X if ((helpfile = strrchr(HELPFILE, '\\')) == NULL) X helpfile = HELPFILE; X else X helpfile++; X return (searchpath(helpfile)); X#else X return (save(HELPFILE)); X#endif X} X X/* X * Expand a string, substituting any "%" with the current filename, X * and any "#" with the previous filename. X */ X public char * Xfexpand(s) X char *s; X{ X register char *fr, *to; X register int n; X register char *e; X X /* X * Make one pass to see how big a buffer we X * need to allocate for the expanded string. X */ X n = 0; X for (fr = s; *fr != '\0'; fr++) X { X switch (*fr) X { X case '%': X n += strlen(get_filename(curr_ifile)); X break; X case '#': X if (old_ifile == NULL_IFILE) X { X error("No previous file", NULL_PARG); X return (NULL); X } X n += strlen(get_filename(old_ifile)); X break; X default: X n++; X break; X } X } X X e = (char *) ecalloc(n+1, sizeof(char)); X X /* X * Now copy the string, expanding any "%" or "#". X */ X to = e; X for (fr = s; *fr != '\0'; fr++) X { X switch (*fr) X { X case '%': X strcpy(to, get_filename(curr_ifile)); X to += strlen(to); X break; X case '#': X strcpy(to, get_filename(old_ifile)); X to += strlen(to); X break; X default: X *to++ = *fr; X break; X } X } X *to = '\0'; X return (e); X} X X/* X * Try to determine if a file is "binary". X * This is just a guess, and we need not try too hard to make it accurate. X */ X int Xbinary_file(f) X int f; X{ X int i; X int n; X char data[64]; X X n = read(f, data, sizeof(data)); X for (i = 0; i < n; i++) X if (binary_char(data[i])) X return (1); X return (0); X} X X/* X * Try to determine the size of a file by seeking to the end. X */ X static POSITION Xseek_filesize(f) X int f; X{ X offset_t spos; X X spos = lseek(f, (offset_t)0, 2); X if (spos == BAD_LSEEK) X return (NULL_POSITION); X return ((POSITION) spos); X} X X/* X * Expand a filename, substituting any environment variables, etc. X */ X#if GLOB X XFILE *popen(); X X public char * Xglob(filename) X char *filename; X{ X FILE *f; X char *p; X int ch; X int len; X char *cmd; X char *gfilename; X X filename = fexpand(filename); X if (filename == NULL) X return (NULL); X X /* X * We get the shell to expand the filename for us by passing X * an "echo" command to the shell and reading its output. X */ X p = getenv("SHELL"); X if (p == NULL || *p == '\0') X { X /* X * Read the output of . X */ X cmd = (char *) ecalloc(strlen(filename)+6, sizeof(char)); X sprintf(cmd, "echo %s", filename); X } else X { X /* X * Read the output of <$SHELL -c "echo filename">. X */ X cmd = (char *) ecalloc(strlen(p)+strlen(filename)+12, sizeof(char)); X sprintf(cmd, "%s -c \"echo %s\"", p, filename); X } X X f = popen(cmd, "r"); X free(cmd); X if (f == NULL) X return (filename); X free(filename); X X len = 100; X gfilename = (char *) ecalloc(len, sizeof(char)); X for (p = gfilename; ; p++) X { X if ((ch = getc(f)) == '\n' || ch == EOF) X break; X if (p - gfilename >= len-1) X { X len *= 2; X *p = '\0'; X p = (char *) ecalloc(len, sizeof(char)); X strcpy(p, gfilename); X free(gfilename); X gfilename = p; X p = gfilename + strlen(gfilename); X } X *p = ch; X } X *p = '\0'; X pclose(f); X if (*gfilename == '\0') X return (NULL); X return (gfilename); X} X X#else X X public char * Xglob(filename) X char *filename; X{ X return (fexpand(filename)); X} X X#endif X X X#if STAT X X#include X#include X X/* X * Returns NULL if the file can be opened and X * is an ordinary file, otherwise an error message X * (if it cannot be opened or is a directory, etc.) X */ X public char * Xbad_file(filename) X char *filename; X{ X register char *m; X struct stat statbuf; X X if (stat(filename, &statbuf) < 0) X return (errno_message(filename)); X X if (force_open) X return (NULL); X X if ((statbuf.st_mode & S_IFMT) == S_IFDIR) X { X static char is_dir[] = " is a directory"; X m = (char *) ecalloc(strlen(filename) + sizeof(is_dir), X sizeof(char)); X strcpy(m, filename); X strcat(m, is_dir); X return (m); X } X if ((statbuf.st_mode & S_IFMT) != S_IFREG) X { X static char not_reg[] = " is not a regular file"; X m = (char *) ecalloc(strlen(filename) + sizeof(not_reg), X sizeof(char)); X strcpy(m, filename); X strcat(m, not_reg); X return (m); X } X X return (NULL); X} X X/* X * Return the size of a file, as cheaply as possible. X * In Unix, we can stat the file. X */ X public POSITION Xfilesize(f) X int f; X{ X struct stat statbuf; X X if (fstat(f, &statbuf) < 0) X /* X * Can't stat; try seeking to the end. X */ X return (seek_filesize(f)); X X return ((POSITION) statbuf.st_size); X} X X#else X X/* X * If we have no way to find out, just say the file is good. X */ X public char * Xbad_file(filename) X char *filename; X{ X return (NULL); X} X X/* X * We can find the file size by seeking. X */ X public POSITION Xfilesize(f) X int f; X{ X return (seek_filesize(f)); X} X X#endif END_OF_FILE echo shar: Extracting \"lsystem.c\" sed "s/^X//" >'lsystem.c' <<'END_OF_FILE' X/* X * Routines to execute other programs. X * Necessarily very OS dependent. X */ X X#include X#include X X#include "less.h" X#include "position.h" X X#if __MSDOS__ X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include Xchar get_swchar(); Xvoid swchar_to_dos(); Xvoid swchar_to_unix(); X#endif X Xextern char *getenv(); X Xextern int screen_trashed; Xextern IFILE curr_ifile; 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 register int inp; X register char *shell; X register char *p; X register char *curr_filename; 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 putstr("!"); X putstr(cmd); X putstr("\n"); X } X X /* X * Close the current input file. X */ X curr_filename = get_filename(curr_ifile); X (void) edit(NULL, 0); X X /* X * De-initialize the terminal and take out of raw mode. X */ X deinit(); X flush(); /* Make sure the deinit chars get out */ X raw_mode(0); X X /* X * Restore signals to their defaults. X */ X init_signals(0); X X /* X * Force standard input to be the user's terminal X * (the normal standard input), even if less's standard input X * is coming from a pipe. X */ X#if __MSDOS__ X{ X register int inp2; X X inp = dup(0); X inp2 = open("CON", O_TEXT|O_RDONLY); X dup2(0,inp2); X} X#else X inp = dup(0); X close(0); X if (open("/dev/tty", 0) < 0) X dup(inp); X#endif X X /* X * Pass the command to the system to be executed. X * If we have a SHELL environment variable, use X * <$SHELL -c "command"> instead of just . X * If the command is empty, just invoke a shell. X */ X#if __MSDOS__ X{ X int result; X char sw_char; X X sw_char = get_swchar(); X swchar_to_dos(); X result = system(cmd); X if (result != 0) X perror("less"); X if (sw_char == '-') X swchar_to_unix(); X} X#else X p = NULL; X if ((shell = getenv("SHELL")) != NULL && *shell != '\0') X { X if (*cmd == '\0') X p = save(shell); X else X { X p = (char *) ecalloc(strlen(shell) + strlen(cmd) + 7, X sizeof(char)); X sprintf(p, "%s -c \"%s\"", shell, cmd); X } X } X if (p == NULL) X { X if (*cmd == '\0') X p = save("sh"); X else X p = save(cmd); X } X X system(p); X free(p); X#endif X X /* X * Restore standard input, reset signals, raw mode, etc. X */ X#if __MSDOS__ X close(inp2); X dup2(0,inp); X close(inp); X#else X close(0); X dup(inp); X close(inp); X#endif X X init_signals(1); X raw_mode(1); X init(); X screen_trashed = 1; X X /* X * Reopen the current input file. X */ X (void) edit(curr_filename, 0); X X#if defined(SIGWINCH) || defined(SIGWIND) X /* X * Since we were ignoring window change signals while we executed X * the system command, we must assume the window changed. X * Warning: this leaves a signal pending (in "sigs"), X * so psignals() should be called soon after lsystem(). X */ X winch(); X#endif X} X X#if PIPEC X X/* X * Pipe a section of the input file into the given shell command. X * The section to be piped is the section "between" the current X * position and the position marked by the given letter. X * X * The "current" position means the top line displayed if the mark X * is after the current screen, or the bottom line displayed if X * the mark is before the current screen. X * If the mark is on the current screen, the whole screen is displayed. X */ X public int Xpipe_mark(c, cmd) X int c; X char *cmd; X{ X POSITION mpos, tpos, bpos; X X /* X * mpos = the marked position. X * tpos = top of screen. X * bpos = bottom of screen. X */ X mpos = markpos(c); X if (mpos == NULL_POSITION) X return (-1); X tpos = position(TOP); X if (tpos == NULL_POSITION) X tpos = ch_zero(); X bpos = position(BOTTOM_PLUS_ONE); X X if (mpos <= tpos) X return (pipe_data(cmd, mpos, bpos)); X else if (bpos == NULL_POSITION || mpos <= bpos) X return (pipe_data(cmd, tpos, bpos)); X else X return (pipe_data(cmd, tpos, mpos)); X} X X/* X * Create a pipe to the given shell command. X * Feed it the file contents between the positions spos and epos. X */ X public int Xpipe_data(cmd, spos, epos) X char *cmd; X POSITION spos; X POSITION epos; X{ X register FILE *f; X register int c; X int inp; X extern FILE *popen(); X X /* X * This is structured much like lsystem(). X * Since we're running a shell program, we must be careful X * to perform the necessary deinitialization before running X * the command, and reinitialization after it. X */ X if (ch_seek(spos) != 0) X { X error("Cannot seek to start position", NULL_PARG); X return (-1); X } X X if ((f = popen(cmd, "w")) == NULL) X { X error("Cannot create pipe", NULL_PARG); X return (-1); X } X lower_left(); X clear_eol(); X putstr("!"); X putstr(cmd); X putstr("\n"); X X deinit(); X flush(); X raw_mode(0); X init_signals(0); X X while (epos == NULL_POSITION || spos++ < epos) X { X /* X * Read a character from the file and give it to the pipe. X */ X c = ch_forw_get(); X if (c == EOI) X break; X putc(c, f); X } X pclose(f); X X init_signals(1); X raw_mode(1); X init(); X screen_trashed = 1; X#if defined(SIGWINCH) || defined(SIGWIND) X /* {{ Probably don't need this here. }} */ X winch(); X#endif X return (0); X} X X#endif END_OF_FILE echo shar: Extracting \"output.c\" sed "s/^X//" >'output.c' <<'END_OF_FILE' 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() */ Xpublic int need_clr; X Xextern int sigs; Xextern int sc_width; Xextern int so_s_width, so_e_width; Xextern int screen_trashed; Xextern int any_display; X#if __MSDOS__ Xextern int output_mode; X#endif X X/* X * Display the line which is in the line buffer. X */ X public void Xput_line() X{ X register int c; X register int i; X int a; X int curr_attr; X X if (sigs) X { X /* X * Don't output if a signal is pending. X */ X screen_trashed = 1; X return; X } X X curr_attr = NORMAL; X X for (i = 0; (c = gline(i, &a)) != '\0'; i++) X { X if (a != curr_attr) X { X /* X * Changing attributes. X * Display the exit sequence for the old attribute X * and the enter sequence for the new one. X */ X switch (curr_attr) X { X case UNDERLINE: ul_exit(); break; X case BOLD: bo_exit(); break; X case BLINK: bl_exit(); break; X } X switch (a) X { X case UNDERLINE: ul_enter(); break; X case BOLD: bo_enter(); break; X case BLINK: bl_enter(); break; X } X curr_attr = a; X } X if (curr_attr == INVIS) X continue; X if (c == '\b') X putbs(); X else X putchr(c); X } X} X Xstatic char obuf[1024]; Xstatic char *ob = obuf; X X/* X * Flush buffered output. X * X * If we haven't displayed any file data yet, X * output messages on error output (file descriptor 2), X * otherwise output on standard output (file descriptor 1). 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 public void Xflush() X{ X register int n; X register int fd; X X#if __MSDOS__ X if (output_mode == 0) X { X *ob = '\0'; X cputs(obuf); X ob = obuf; X return; X } X#endif X n = ob - obuf; X if (n == 0) X return; X fd = (any_display) ? 1 : 2; X if (write(fd, obuf, n) != n) X screen_trashed = 1; X ob = obuf; X} X X/* X * Output a character. X */ X public void Xputchr(c) X int c; X{ X if (ob >= &obuf[sizeof(obuf)]) X flush(); X if (need_clr) X { X need_clr = 0; X lower_left(); X clear_eol(); X } X#if __MSDOS__ X if (c == '\n') X *ob++ = '\r'; X#endif X *ob++ = c; X} X X/* X * Output a string. X */ X public void Xputstr(s) X register char *s; X{ X while (*s != '\0') X putchr(*s++); X} X X X/* X * Output an integer in a given radix. X */ X static int Xiprintnum(num, radix) X int num; X int radix; X{ X register char *s; X int r; X int neg; X char buf[10]; X X if (neg = (num < 0)) X num = -num; X X s = buf; X do X { X *s++ = (num % radix) + '0'; X } while ((num /= radix) != 0); X X if (neg) X *s++ = '-'; X r = s - buf; X X while (s > buf) X putchr(*--s); X return (r); X} X X/* X * This function implements printf-like functionality X * using a more portable argument list mechanism than printf's. X */ X static int Xiprintf(fmt, parg) X register char *fmt; X PARG *parg; X{ X register char *s; X register int n; X register int col; X X col = 0; X while (*fmt != '\0') X { X if (*fmt != '%') X { X putchr(*fmt++); X col++; X } else X { X ++fmt; X switch (*fmt++) { X case 's': X s = parg->p_string; X parg++; X while (*s != '\0') X { X putchr(*s++); X col++; X } X break; X case 'd': X n = parg->p_int; X parg++; X col += iprintnum(n, 10); X break; X } X } X } X return (col); X} X X/* X * Output a message in the lower left corner of the screen X * and wait for carriage return. X */ X public void Xerror(fmt, parg) X char *fmt; X PARG *parg; X{ X int c; X int col = 0; X static char return_to_continue[] = " (press RETURN)"; X X errmsgs++; X X if (any_display) X { X lower_left(); X clear_eol(); X so_enter(); X col += so_s_width; X } X X col += iprintf(fmt, parg); X X if (!any_display) X { X putchr('\n'); X return; X } X X putstr(return_to_continue); X so_exit(); X col += sizeof(return_to_continue) + so_e_width; X X#if ONLY_RETURN X while ((c = getchr()) != '\n' && c != '\r') X bell(); X#else X c = getchr(); X if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) X ungetcc(c); X#endif X lower_left(); X X if (col >= 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 screen_trashed = 1; X X flush(); X} X Xstatic char intr_to_abort[] = "... (interrupt to abort)"; X X/* X * Output a message in the lower left corner of the screen X * and don't wait for carriage return. X * Usually used to warn that we are beginning a potentially X * time-consuming operation. X */ X public void Xierror(fmt, parg) X char *fmt; X PARG *parg; X{ X lower_left(); X clear_eol(); X so_enter(); X (void) iprintf(fmt, parg); X putstr(intr_to_abort); X so_exit(); X flush(); X need_clr = 1; X} X X/* X * Output a message in the lower left corner of the screen X * and return a single-character response. X */ X public int Xquery(fmt, parg) X char *fmt; X PARG *parg; X{ X register int c; X int col = 0; X X if (any_display) X { X lower_left(); X clear_eol(); X } X X (void) iprintf(fmt, parg); X c = getchr(); X X if (!any_display) X { X putchr('\n'); X return (c); X } X X lower_left(); X if (col >= sc_width) X screen_trashed = 1; X flush(); X X return (c); X} END_OF_FILE echo shar: Extracting \"position.c\" sed "s/^X//" >'position.c' <<'END_OF_FILE' 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 Xstatic POSITION *table = NULL; /* The position table */ Xstatic int table_size; 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 case MIDDLE: X where = sc_height / 2; 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 * Allocate the position table. X */ X public void Xpos_init() X{ X if (sc_height <= table_size) X return; X if (table != NULL) X free((char*)table); X table = (POSITION *) ecalloc(sc_height, sizeof(POSITION)); X table_size = sc_height; 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} X X/* X * See if the entire screen is empty. X */ X public int Xempty_screen() X{ X return (empty_lines(0, sc_height-1)); X} X X public int Xempty_lines(s, e) X int s; X int e; X{ X register int i; X X for (i = s; i <= e; i++) X if (table[i] != NULL_POSITION) X return (0); X return (1); X} X X/* X * Get the current screen position. X * The screen position consists of both a file position and X * a screen line number where the file position is placed on the screen. X * Normally the screen line number is 0, but if we are positioned X * such that the top few lines are empty, we may have to set X * the screen line to a number > 0. X */ X public void Xget_scrpos(scrpos) X struct scrpos *scrpos; X{ X register int i; X X /* X * Find the first line on the screen which has something on it, X * and return the screen line number and the file position. X */ X for (i = 0; i < sc_height; i++) X if (table[i] != NULL_POSITION) X { X scrpos->ln = i+1; X scrpos->pos = table[i]; X return; X } X /* X * The screen is empty. X */ X scrpos->pos = NULL_POSITION; X} X X/* X * Adjust a screen line number to be a simple positive integer X * in the range { 0 .. sc_height-2 }. X * (The bottom line, sc_height-1, is reserved for prompts, etc.) X * The given "sline" may be in the range { 1 .. sc_height-1 } X * to refer to lines relative to the top of the screen (starting from 1), X * or it may be in { -1 .. -(sc_height-1) } to refer to lines X * relative to the bottom of the screen. X */ X public int Xadjsline(sline) X int sline; X{ X /* X * Negative screen line number means X * relative to the bottom of the screen. X */ X if (sline < 0) X sline += sc_height; X /* X * Can't be less than 1 or greater than sc_height-1. X */ X if (sline <= 0) X sline = 1; X if (sline >= sc_height) X sline = sc_height - 1; X /* X * Return zero-based line number, not one-based. X */ X return (sline-1); X} END_OF_FILE echo shar: Extracting \"ifile.c\" sed "s/^X//" >'ifile.c' <<'END_OF_FILE' X/* X * An IFILE represents an input file. X * X * It is actually a pointer to an ifile structure, X * but is opaque outside this module. X * Ifile structures are kept in a linked list in the order they X * appear on the command line. X * Any new file which does not already appear in the list is X * inserted after the current file. X */ X X#include "less.h" X Xstruct ifile { X struct ifile *h_next; /* Links for command line list */ X struct ifile *h_prev; X int h_index; /* Index within command line list */ X char *h_filename; /* Name of the file */ X struct scrpos h_scrpos; /* Saved position within the file */ X}; X X/* X * Convert an IFILE (external representation) X * to a struct file (internal representation), and vice versa. X */ X#define int_ifile(h) ((struct ifile *)(h)) X#define ext_ifile(h) ((IFILE)(h)) X X/* X * Anchor for linked list. X */ Xstatic struct ifile anchor = { &anchor, &anchor, 0 }; Xstatic int ifiles = 0; X X/* X * Allocate a new ifile structure and stick a filename in it. X * It should go after "prev" in the list X * (or at the beginning of the list if "prev" is NULL). X * Return a pointer to the new ifile structure. X */ X static struct ifile * Xnew_ifile(filename, prev) X char *filename; X struct ifile *prev; X{ X register struct ifile *p; X register struct ifile *np; X X /* X * Allocate and initialize structure. X */ X p = (struct ifile *) ecalloc(1, sizeof(struct ifile)); X p->h_filename = filename; X p->h_scrpos.pos = NULL_POSITION; X X /* X * Link into list. X */ X if (prev == NULL) X prev = &anchor; X p->h_next = prev->h_next; X p->h_prev = prev; X prev->h_next->h_prev = p; X prev->h_next = p; X X /* X * Calculate index for the new one, X * and adjust the indexes for subsequent ifiles in the list. X */ X p->h_index = prev->h_index + 1; X for (np = p->h_next; np != &anchor; np = np->h_next) X np->h_index++; X X ifiles++; X return (p); X} X X/* X * Get the ifile after a given one in the list. X */ X public IFILE Xnext_ifile(h) X IFILE h; X{ X register struct ifile *p; X X p = (h == NULL_IFILE) ? &anchor : int_ifile(h); X if (p->h_next == &anchor) X return (NULL_IFILE); X return (ext_ifile(p->h_next)); X} X X/* X * Get the ifile before a given one in the list. X */ X public IFILE Xprev_ifile(h) X IFILE h; X{ X register struct ifile *p; X X p = (h == NULL_IFILE) ? &anchor : int_ifile(h); X if (p->h_prev == &anchor) X return (NULL_IFILE); X return (ext_ifile(p->h_prev)); X} X X/* X * Return the number of ifiles. X */ X public int Xnifile() X{ X return (ifiles); X} X X/* X * Find an ifile structure, given a filename. X */ X static struct ifile * Xfind_ifile(filename) X char *filename; X{ X register struct ifile *p; X X for (p = anchor.h_next; p != &anchor; p = p->h_next) X if (strcmp(filename, p->h_filename) == 0) X return (p); X return (NULL); X} X X/* X * Get the ifile associated with a filename. X * If the filename has not been seen before, X * insert the new ifile after "prev" in the list. X */ X public IFILE Xget_ifile(filename, prev) X char *filename; X IFILE prev; X{ X register struct ifile *p; X X if ((p = find_ifile(filename)) == NULL) X p = new_ifile(save(filename), int_ifile(prev)); X return (ext_ifile(p)); X} X X/* X * Get the filename associated with a ifile. X */ X public char * Xget_filename(ifile) X IFILE ifile; X{ X return (int_ifile(ifile)->h_filename); X} X X/* X * Get the index of the file associated with a ifile. X */ X public int Xget_index(ifile) X IFILE ifile; X{ X return (int_ifile(ifile)->h_index); X} X X/* X * Save the file position to be associated with a given file. X */ X public void Xstore_pos(ifile, scrpos) X IFILE ifile; X struct scrpos *scrpos; X{ X int_ifile(ifile)->h_scrpos = *scrpos; X} X X/* X * Recall the file position associated with a file. X * If no position has been associated with the file, return NULL_POSITION. X */ X public void Xget_pos(ifile, scrpos) X IFILE ifile; X struct scrpos *scrpos; X{ X *scrpos = int_ifile(ifile)->h_scrpos; X} END_OF_FILE echo shar: Extracting \"brac.c\" sed "s/^X//" >'brac.c' <<'END_OF_FILE' X/* X * Routines to perform bracket matching functions. X */ X X#include "less.h" X#include "position.h" X X/* X * Try to match the n-th open bracket X * which appears in the top displayed line (forwdir), X * or the n-th close bracket X * which appears in the bottom displayed line (!forwdir). X * The characters which serve as "open bracket" and X * "close bracket" are given. X */ X public void Xmatch_brac(obrac, cbrac, forwdir, n) X register int obrac; X register int cbrac; X int forwdir; X int n; X{ X register int c; X register int nest; X POSITION pos; X int (*chget)(); X X extern int ch_forw_get(), ch_back_get(); X X /* X * Seek to the line containing the open bracket. X * This is either the top or bottom line on the screen, X * depending on the type of bracket. X */ X pos = position((forwdir) ? TOP : BOTTOM); X if (pos == NULL_POSITION || ch_seek(pos)) X { X if (forwdir) X error("Nothing in top line", NULL_PARG); X else X error("Nothing in bottom line", NULL_PARG); X return; X } X X /* X * Look thru the line to find the open bracket to match. X */ X do X { X if ((c = ch_forw_get()) == '\n' || c == EOI) X { X if (forwdir) X error("No bracket in top line", NULL_PARG); X else X error("No bracket in bottom line", NULL_PARG); X return; X } X } while (c != obrac || --n > 0); X X /* X * Position the file just "after" the open bracket X * (in the direction in which we will be searching). X * If searching forward, we are already after the bracket. X * If searching backward, skip back over the open bracket. X */ X if (!forwdir) X (void) ch_back_get(); X X /* X * Search the file for the matching bracket. X */ X chget = (forwdir) ? ch_forw_get : ch_back_get; X nest = 0; X while ((c = (*chget)()) != EOI) X { X if (c == obrac) X nest++; X else if (c == cbrac && --nest < 0) X { X /* X * Found the matching bracket. X * If searching backward, put it on the top line. X * If searching forward, put it on the bottom line. X */ X jump_line_loc(ch_tell(), forwdir ? -1 : 1); X return; X } X } X error("No matching bracket", NULL_PARG); X} END_OF_FILE echo shar: Extracting \"forwback.c\" sed "s/^X//" >'forwback.c' <<'END_OF_FILE' X/* X * Primitives for displaying the file on the screen, X * scrolling either forward or backward. 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 */ Xpublic int screen_trashed; Xpublic int squished; X Xextern int sigs; Xextern int top_scroll; Xextern int quiet; Xextern int sc_width, sc_height; Xextern int quit_at_eof; Xextern int plusoption; Xextern int forw_scroll; Xextern int back_scroll; Xextern int need_clr; Xextern int ignore_eoi; X#if TAGS Xextern int tagoption; X#endif X X/* X * Sound the bell to indicate user 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 if (sigs) X return; 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 * If the screen is "squished", repaint it. X * "Squished" means the first displayed line is not at the top X * of the screen; this can happen when we display a short file X * for the first time. X */ X static void Xsquish_check() X{ X if (!squished) X return; X squished = 0; X repaint(); 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 * "nblank" is the number of blank lines to draw before the first X * real line. If nblank > 0, the pos must be NULL_POSITION. X * The first real line after the blanks will start at ch_zero(). X */ X public void Xforw(n, pos, force, only_last, nblank) X register int n; X POSITION pos; X int force; X int only_last; X int nblank; X{ X int eof = 0; X int nlines = 0; X int do_repaint; X static int first_time = 1; X X squish_check(); X X /* X * do_repaint tells us not to display anything till the end, X * then just repaint the entire screen. X * We repaint if we are supposed to display only the last X * screenful and the request is for more than a screenful. X * Also if the request exceeds the forward scroll limit X * (but not if the request is for exactly a screenful, since X * repainting itself involves scrolling forward a screenful). X */ X do_repaint = (only_last && n > sc_height-1) || X (forw_scroll >= 0 && n > forw_scroll && n != sc_height-1); X X if (!do_repaint) X { X if (top_scroll && n >= sc_height - 1 && pos != ch_length()) 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 if (top_scroll == 2 || first_time) 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) || empty_screen()) 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 if (top_scroll == 2) X clear(); X home(); X } else if (!first_time) X { X putstr("...skipping...\n"); X } X } X } X X while (--n >= 0) X { X /* X * Read the next line of input. X */ X if (nblank > 0) X { X /* X * Still drawing blanks; don't get a line X * from the file yet. X * If this is the last blank line, get ready to X * read a line starting at ch_zero() next time. X */ X if (--nblank == 0) X pos = ch_zero(); X } else X { X /* X * Get the next line from the file. 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 } 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 (do_repaint) X continue; X /* X * If this is the first screen displayed and X * we hit an early EOF (i.e. before the requested X * number of lines), we "squish" the display down X * at the bottom of the screen. X * But don't do this if a + option or a -t option X * was given. These options can cause us to X * start the display after the beginning of the file, X * and it is not appropriate to squish in that case. X */ X if (first_time && pos == NULL_POSITION && !top_scroll && X#if TAGS X !tagoption && X#endif X !plusoption) X { X squished = 1; X continue; X } X if (top_scroll == 1) X clear_eol(); X put_line(); X } X X if (eof && !sigs) X hit_eof++; X else X eof_check(); X if (nlines == 0) X eof_bell(); X else if (do_repaint) X repaint(); X first_time = 0; X (void) currline(BOTTOM); X} X X/* X * Display n lines, scrolling backward. X */ X public 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 do_repaint; X X squish_check(); X do_repaint = (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 } 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 (!do_repaint) 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 (do_repaint) X repaint(); X (void) currline(BOTTOM); 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, force, only_last) X int n; X int force; X int only_last; X{ X POSITION pos; X X if (quit_at_eof && hit_eof) X { X /* X * If the -e flag is set and we're trying to go X * forward from end-of-file, go on to the next file. X */ X if (edit_next(1)) X quit(0); X return; X } X X pos = position(BOTTOM_PLUS_ONE); X if (pos == NULL_POSITION && (!force || empty_lines(2, sc_height-1))) X { X if (ignore_eoi) X { X /* X * ignore_eoi is to support A_F_FOREVER. X * Back up until there is a line at the bottom X * of the screen. X */ X if (empty_screen()) X pos = ch_zero(); X else X { X do X { X back(1, position(TOP), 1, 0); X pos = position(BOTTOM_PLUS_ONE); X } while (pos == NULL_POSITION); X } X } else X { X eof_bell(); X hit_eof++; X return; X } X } X forw(n, pos, force, only_last, 0); 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, force, only_last) X int n; X int force; X int only_last; X{ X POSITION pos; X X pos = position(TOP); X if (pos == NULL_POSITION && (!force || position(BOTTOM) == 0)) X { X eof_bell(); X return; X } X back(n, pos, force, only_last); 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 (back_scroll); X if (top_scroll) X return (sc_height - 2); X return (10000); /* infinity */ X} END_OF_FILE echo shar: Extracting \"jump.c\" sed "s/^X//" >'jump.c' <<'END_OF_FILE' X/* X * Routines which jump to a new location in the file. X */ X X#include "less.h" X#include "position.h" X Xextern int hit_eof; Xextern int jump_sline; Xextern int squished; Xextern int screen_trashed; Xextern int sc_width, sc_height; X X/* X * Jump to the end of the file. X */ X public void Xjump_forw() X{ X if (ch_end_seek()) X { X error("Cannot seek to end of file", NULL_PARG); X return; X } X /* X * Position the last line in the file at the last screen line. X */ X jump_loc(back_line(ch_tell()), sc_height-1); X} X X/* X * Jump to line n in the file. X */ X public void Xjump_back(n) X int n; X{ X POSITION pos; X PARG parg; X X /* X * Find the position of the specified line. X * If we can seek there, just jump to it. X * If we can't seek, but we're trying to go to line number 1, X * use ch_beg_seek() to get as close as we can. X */ X pos = find_pos(n); X if (pos != NULL_POSITION && ch_seek(pos) == 0) X { X jump_loc(pos, jump_sline); X } else if (n <= 1 && ch_beg_seek() == 0) X { X jump_loc(ch_tell(), jump_sline); X error("Cannot seek to beginning of file", NULL_PARG); X } else X { X parg.p_int = n; X error("Cannot seek to line number %d", &parg); X } X} X X/* X * Repaint the screen. X */ X public void Xrepaint() X{ X struct scrpos scrpos; X /* X * Start at the line currently at the top of the screen X * and redisplay the screen. X */ X get_scrpos(&scrpos); X pos_clear(); X jump_loc(scrpos.pos, scrpos.ln); X} X X/* X * Jump to a specified percentage into the file. X */ X public void Xjump_percent(percent) X int percent; X{ X POSITION pos, len; 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", NULL_PARG); X return; X } X /* X * {{ This calculation may overflow! }} X */ X pos = (percent * len) / 100; X if (pos >= len) X pos = len-1; X X jump_line_loc(pos, jump_sline); X} X X/* X * Jump to a specified position in the file. X * Like jump_loc, but the position need not be X * the first character in a line. X */ X public void Xjump_line_loc(pos, sline) X POSITION pos; X int sline; X{ X int c; X X if (ch_seek(pos) == 0) X { X /* X * Back up to the beginning of the line. X */ X while ((c = ch_back_get()) != '\n' && c != EOI) X ; X if (c == '\n') X (void) ch_forw_get(); X pos = ch_tell(); X } X jump_loc(pos, sline); X} X X/* X * Jump to a specified position in the file. X * The position must be the first character in a line. X * Place the target line on a specified line on the screen. X */ X public void Xjump_loc(pos, sline) X POSITION pos; X int sline; X{ X register int nline; X POSITION tpos; X POSITION bpos; X X /* X * Normalize sline. X */ X sline = adjsline(sline); X X if ((nline = onscreen(pos)) >= 0) X { X /* X * The line is currently displayed. X * Just scroll there. X */ X nline -= sline; X if (nline > 0) X forw(nline, position(BOTTOM_PLUS_ONE), 1, 0, 0); X else X back(-nline, position(TOP), 1, 0); X return; X } X X /* X * Line is not on screen. X * Seek to the desired location. X */ X if (ch_seek(pos)) X { X error("Cannot seek to that file position", NULL_PARG); X return; X } X X /* X * See if the desired line is before or after X * the currently displayed screen. X */ X tpos = position(TOP); X bpos = position(BOTTOM_PLUS_ONE); X if (tpos == NULL_POSITION || pos >= tpos) X { X /* X * The desired line is after the current screen. X * Move back in the file far enough so that we can X * call forw() and put the desired line at the X * sline-th line on the screen. X */ X for (nline = 0; nline < sline; nline++) X { X if (bpos != NULL_POSITION && pos <= bpos) X { X /* X * Surprise! The desired line is X * close enough to the current screen X * that we can just scroll there after all. X */ X forw(sc_height-sline+nline-1, bpos, 1, 0, 0); X return; X } X pos = back_line(pos); X if (pos == NULL_POSITION) X { X /* X * Oops. Ran into the beginning of the file. X * Exit the loop here and rely on forw() X * below to draw the required number of X * blank lines at the top of the screen. X */ X break; X } X } X lastmark(); X hit_eof = 0; X squished = 0; X screen_trashed = 0; X forw(sc_height-1, pos, 1, 0, sline-nline); X } else X { X /* X * The desired line is before the current screen. X * Move forward in the file far enough so that we X * can call back() and put the desired line at the X * sline-th line on the screen. X */ X for (nline = sline; nline < sc_height - 1; nline++) X { X pos = forw_line(pos); X if (pos == NULL_POSITION) X { X /* Cannot happen! */ X error("Program error: EOI in jump_loc (forw)", X NULL_PARG); X quit(1); X } X if (pos >= tpos) X { X /* X * Surprise! The desired line is X * close enough to the current screen X * that we can just scroll there after all. X */ X back(nline+1, tpos, 1, 0); X return; X } X } X lastmark(); X clear(); X screen_trashed = 0; X add_back_pos(pos); X back(sc_height-1, pos, 1, 0); X } X} END_OF_FILE echo shar: Extracting \"search.c\" sed "s/^X//" >'search.c' <<'END_OF_FILE' X/* X * Routines to search a file for a pattern. X */ X X#include "less.h" X#include "position.h" X#if REGCOMP X#include "regexp.h" X#endif X Xextern int sigs; Xextern int how_search; Xextern int top_scroll; Xextern int back_scroll; Xextern int caseless; Xextern int linenums; Xextern int sc_height; Xextern int jump_sline; X X/* X * Search for the n-th occurrence of a specified pattern, X * either forward or backward. X * Return the number of matches not yet found in this file X * (that is, n minus the number of matches found). X * Return -1 if the search should be aborted. X * Caller may continue the search in another file X * if less than n matches are found in this file. X */ X public int Xsearch(search_type, pattern, n) X int search_type; X char *pattern; X int n; X{ X POSITION pos, linepos; X register char *p; X register char *q; X register int goforw; X register int want_match; X char *line; X int linenum; X int line_match; X static int is_caseless; X#if RECOMP X char *re_comp(); X PARG parg; X#else X#if REGCMP X char *regcmp(); X static char *cpattern = NULL; X#else X#if REGCOMP X static struct regexp *regpattern = NULL; X#else X static char lpbuf[100]; X static char *last_pattern = NULL; X#endif X#endif X#endif X X /* X * Extract flags and type of search. X */ X goforw = (SRCH_DIR(search_type) == SRCH_FORW); X want_match = !(search_type & SRCH_NOMATCH); X X if (pattern != NULL && (is_caseless = caseless)) X { X /* X * Search will ignore case, unless X * there are any uppercase letters in the pattern. X */ X for (p = pattern; *p != '\0'; p++) X if (*p >= 'A' && *p <= 'Z') X { X is_caseless = 0; X break; X } X } X#if RECOMP 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 ((parg.p_string = re_comp(pattern)) != NULL) X { X error("%s", &parg); X return (-1); X } X#else X#if REGCMP 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", NULL_PARG); X return (-1); 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", NULL_PARG); X return (-1); X } X if (cpattern != NULL) X free(cpattern); X cpattern = s; X } X#else X#if REGCOMP X if (pattern == NULL || *pattern == '\0') X { X /* X * A null pattern means use the previous pattern. X * The compiled previous pattern is in regpattern, X * so just use it. X */ X if (regpattern == NULL) X { X error("No previous regular expression", NULL_PARG); X return (-1); X } X } else X { X /* X * Otherwise compile the given pattern. X */ X struct regexp *s; X if ((s = regcomp(pattern)) == NULL) X { X error("Invalid pattern", NULL_PARG); X return (-1); X } X if (regpattern != NULL) X free(regpattern); X regpattern = s; X } X#else 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", NULL_PARG); X return (-1); X } X pattern = last_pattern; X } else X { X strcpy(lpbuf, pattern); X last_pattern = lpbuf; X } X#endif X#endif X#endif X X /* X * Figure out where to start the search. X */ X if (empty_screen()) X { X /* X * Start at the beginning (or end) of the file. X * (The empty_screen() case is mainly for X * command line initiated searches; X * for example, "+/xyz" on the command line.) X */ X if (goforw) X pos = ch_zero(); X else X { X pos = ch_length(); X if (pos == NULL_POSITION) X pos = ch_zero(); X } X } else X { X if (how_search) X { X if (goforw) X linenum = BOTTOM_PLUS_ONE; X else X linenum = TOP; X pos = position(linenum); X } else X { X linenum = adjsline(jump_sline); X pos = position(linenum); X if (goforw) X pos = forw_raw_line(pos, (char **)NULL); X } X } X X if (pos == NULL_POSITION) X { X /* X * Can't find anyplace to start searching from. X */ X error("Nothing to search", NULL_PARG); X return (-1); X } X X linenum = find_linenum(pos); 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 (-1); X X if (goforw) 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, &line); X if (linenum != 0) X linenum++; 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, &line); X linepos = pos; X if (linenum != 0) X linenum--; X } X X if (pos == NULL_POSITION) X { X /* X * We hit EOF/BOF without a match. X */ X return (n); X } X X /* X * If we're using line numbers, we might as well X * remember the information we have now (the position X * and line number of the current line). X */ X if (linenums) X add_lnum(linenum, pos); X X if (is_caseless) X { X /* X * If this is a caseless search, convert X * uppercase in the input line to lowercase. X * While we're at it, remove any backspaces X * along with the preceding char. X * This allows us to match text which is X * underlined or overstruck. X */ X for (p = q = line; *p != '\0'; p++, q++) X { X if (*p >= 'A' && *p <= 'Z') X /* Convert uppercase to lowercase. */ X *q = *p + 'a' - 'A'; X else if (q > line && *p == '\b') X /* Delete BS and preceding char. */ X q -= 2; X else X /* Otherwise, just copy. */ X *q = *p; X } 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 line_match = (regex(cpattern, line) != NULL); X#else X#if RECOMP X line_match = (re_exec(line) == 1); X#else X#if REGCOMP X linematch = regexec(regpattern, line); X#else X line_match = match(pattern, line); X#endif X#endif X#endif X /* X * We are successful if want_match and line_match are X * both true (want a match and got it), X * or both false (want a non-match and got it). X */ X if (((want_match && line_match) || (!want_match && !line_match)) && X --n <= 0) X /* X * Found the line. X */ X break; X } X X jump_loc(linepos, jump_sline); X return (0); X} X X#if (!REGCMP) && (!RECOMP) && (!REGCOMP) 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 END_OF_FILE echo shar: Extracting \"less.hlp\" sed "s/^X//" >'less.hlp' <<'END_OF_FILE' X X SUMMARY OF COMMANDS X X Commands marked with * may be preceded by a number, N. X Notes in parentheses indicate the behavior if N is given. X X h H Display this help. X q :q :Q ZZ Exit. X X e ^E j ^N CR * Forward one line (or N lines). X y ^Y k ^K ^P * Backward one line (or N lines). X f ^F ^V SPACE * Forward one window (or N lines). X b ^B ESC-v * Backward one window (or N lines). X z * Forward one window (and set window to N). X w * Backward one window (and set window to N). X d ^D * Forward one half-window (and set half-window to N). X u ^U * Backward one half-window (and set half-window to N). X F Forward forever; like "tail -f". X r ^R ^L Repaint screen. X R Repaint screen, discarding buffered input. X X NOTE: default "window" is the screen height. X default "half-window" is half of the screen height. X X /pattern * Search forward for (N-th) matching line. X ?pattern * Search backward for (N-th) matching line. X ESC-/pattern * Search all files for (N-th) matching line. X X /!pattern * Search forward for (N-th) NON-matching line. X ?!pattern * Search backward for (N-th) NON-matching line. X ESC-/!pattern * Search from all files for (N-th) NON-matching line. X X n * Repeat previous search (for N-th occurrence). X N * Repeat previous search in reverse direction. X ESC-n * Repeat previous search, spanning files. X ESC-N * Repeat previous search, reverse dir. & spanning files. X X g < ESC-< * Go to first line in file (or line N). X G > ESC-> * Go to last line in file (or line N). X p % * Go to beginning of file (or N percent into file). X { * Go to the } which matches the (N-th) { in the top line. X } * Go to the { which matches the (N-th) } in the top line. X ( * Go to the ) which matches the (N-th) ( in the top line. X ) * Go to the ( which matches the (N-th) ) in the top line. X [ * Go to the ] which matches the (N-th) [ in the top line. X ] * Go to the [ which matches the (N-th) ] in the top line. X m Mark the current position with . X ' Go to a previously marked position. X '' Go to the previous position. X ^X^X Same as '. X X E [file] Examine a new file. X :e ^X^V Same as E. X :n * Examine the (N-th) next file from the command line. X :p * Examine the (N-th) previous file from the command line. X = ^G :f Print current file name. X V Print version number of "less". X X - Toggle a command line flag [see FLAGS below]. X _ Display the setting of a command line flag. X +cmd Execute the less cmd each time a new file is examined. X X !command Passes the command to $SHELL to be executed. X |Xcommand Pipe file between current pos & mark X to shell command. X v Edit the current file with $EDITOR. X X X FLAGS X X Most flags may be changed either on the command line, X or from within less by using the - command. X X -a Set forward search starting location. X -b [N] Number of buffers. X -B Automatically allocate buffers. X -c -C Repaint by scrolling/clearing. X -d Dumb terminal. X -e -E Quit at end of file. X -f Force open non-regular files. X -h [N] Backward scroll limit. X -i Ignore case in searches. X -j [N] Screen position of target lines. X -k [file] Use a lesskey file. X -l [file] Log file. X -L [file] Log file (unconditionally overwrite). X -m -M Set prompt style. X -n -N Use line numbers. X -P [prompt] Define new prompt. X -q -Q Quiet the terminal bell. X -r -R Translate control characters. X -s Squeeze multiple blank lines. X -t [tag] Find a tag. X -T [tagsfile] Use an alternate tags file. X -u -U Change handling of backspaces. X -w Display ~ for lines after end-of-file. X -x [N] Set tab stops. X -y [N] Forward scroll limit. X -z [N] Set size of window. X END_OF_FILE