BSD 3 release
[unix-history] / usr / src / cmd / more.c
#include <whoami.h>
#include <stdio.h>
#include <signal.h>
#include <sgtty.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/stat.h>
#ifdef CORY
#define MBIT RAW
#else
#include <ctype.h>
#define MBIT CBREAK
#endif
#define TBUFSIZ 1024
#define LINSIZ 256
#define ctrl(letter) ('letter' & 077)
#define RUBOUT '\177'
#define ESC '\033'
#define QUIT '\034'
struct sgttyb otty;
int fnum, no_intty, no_tty, slow_tty;
int dum_opt, dlines, onquit(), end_it();
int stop_opt = 1;
int promptlen;
int startup = 1;
int firstf = 1;
int notell = 1;
int inwait, pause, errors;
int within; /* true if we are within a file,
false if we are between files */
int hard, dumb, noscroll, hardtabs;
char **fnames;
int nfiles;
char *shell;
char ch;
jmp_buf restore;
char obuf[BUFSIZ]; /* stdout buffer */
char Line[LINSIZ];
int Lpp = 24; /* lines per page */
char *Clear; /* clear screen */
char *eraseln; /* erase line */
char *Senter, *Sexit;/* enter and exit standout mode */
char *tgetstr();
int Mcol = 80; /* number of columns */
int Wrap = 1; /* set if automargins */
extern char PC; /* pad character */
extern short ospeed;
main(argc, argv)
int argc;
char *argv[];
{
register FILE *f;
register char *s;
register char *p;
register char ch;
register int left;
int prnames = 0;
int initopt = 0;
int srchopt = 0;
int initline;
char buf[TBUFSIZ];
char clearbuf[100];
char initbuf[80];
char *clearptr;
char *getenv();
FILE *checkf();
nfiles = argc;
fnames = argv;
/* Put terminal setup stuff in separate procedure ?? (From here...) */
setbuf(stdout, obuf);
if (!(no_tty = gtty(1, &otty))) {
if (tgetent(buf, getenv("TERM")) <= 0) {
dumb++;
}
else {
if (((Lpp = tgetnum("li")) < 0) || tgetflag("hc")) {
hard++; /* Hard copy terminal */
Lpp = 24;
}
if (!hard && tgetflag("ns"))
noscroll++;
if ((Mcol = tgetnum("co")) < 0)
Mcol = 80;
Wrap = tgetflag("am");
clearptr = clearbuf;
eraseln = tgetstr("ce",&clearptr);
Clear = tgetstr("cl", &clearptr);
Senter = tgetstr("so", &clearptr);
Sexit = tgetstr("se", &clearptr);
PC = *tgetstr("pc", &clearptr);
}
if ((shell = getenv("SHELL")) == NULL)
shell = "/bin/sh";
}
no_intty = gtty(0, &otty);
gtty(2, &otty);
ospeed = otty.sg_ospeed;
slow_tty = ospeed < B1200;
hardtabs = !(otty.sg_flags & XTABS);
if (!no_tty) {
otty.sg_flags &= ~ECHO;
if (MBIT == CBREAK || !slow_tty)
otty.sg_flags |= MBIT;
}
/* ... until here or so */
while (--nfiles > 0) {
if ((ch = (*++fnames)[0]) == '-') {
for (s = fnames[0] + 1, dlines = 0; *s != '\0'; s++)
if (isdigit(*s))
dlines = dlines*10 + *s - '0';
else if (*s == 'd')
dum_opt = 1;
else if (*s == 'l')
stop_opt = 0;
}
else if (ch == '+') {
s = *fnames;
if (*++s == '/') {
srchopt++;
for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';)
*p++ = *s++;
*p = '\0';
}
else {
initopt++;
for (initline = 0; *s != '\0'; s++)
if (isdigit (*s))
initline = initline*10 + *s -'0';
--initline;
}
}
else break;
}
if (dlines == 0)
dlines = Lpp - 2;
left = dlines;
if (nfiles > 1)
prnames++;
if (!no_intty && nfiles == 0) {
fputs("Usage: ",stderr);
fputs(argv[0],stderr);
fputs(" [-dn] name1 name2 ...\n",stderr);
exit(1);
}
else
f = stdin;
if (!no_tty) {
signal(SIGQUIT, onquit);
signal(SIGINT, end_it);
stty (2, &otty);
}
if (no_intty) {
if (no_tty)
copy_file (stdin);
else {
if (srchopt)
search (initbuf, stdin, 1);
else if (initopt)
skiplns (initline, stdin);
screen (stdin, left);
}
end_it ();
}
while (fnum < nfiles) {
if ((f = checkf (fnames[fnum])) != NULL) {
if (firstf) setjmp (restore);
if (firstf) {
firstf = 0;
if (srchopt)
search (initbuf, f, 1);
else if (initopt)
skiplns (initline, f);
}
else if (fnum < nfiles && !no_tty) {
setjmp (restore);
left = command (fnames[fnum], f);
}
if (left != 0) {
if (prnames) {
pr("::::::::::::::");
if (promptlen > 14)
erase (14);
putchar ('\n');
pr(fnames[fnum]);
pr("\n::::::::::::::\n");
if (left > Lpp - 4)
left = Lpp - 4;
}
if (no_tty)
copy_file (f);
else {
within++;
screen(f, left);
within = 0;
}
}
setjmp (restore);
fflush(stdout);
fclose(f);
}
fnum++;
firstf = 0;
}
otty.sg_flags |= ECHO;
otty.sg_flags &= ~MBIT;
stty(2, &otty);
exit(0);
}
/*
** Check whether the file named by fs is an ASCII file which the user may
** access. If it is, return the opened file. Otherwise return NULL.
*/
FILE *
checkf (fs)
register char *fs;
{
#ifdef CORY
int space[3]; /* Why doesn't libretro have a V7 stat? */
#endif
struct stat stbuf;
register FILE *f;
char c;
if (stat (fs, &stbuf) == -1) {
fflush(stdout);
perror(fs);
return (NULL);
}
if (stbuf.st_mode & S_IFDIR) {
pr("\n*** ");
pr(fs);
pr(": directory ***\n\n");
return (NULL);
}
if ((f=fopen(fs, "r")) == NULL) {
fflush(stdout);
perror(fs);
return (NULL);
}
c = getc(f);
/* Try to see whether it is an ASCII file */
switch ((c | *f->_ptr << 8) & 0177777) {
case 0405:
case 0407:
case 0410:
case 0411:
case 0177545:
pr("\n******** ");
pr(fs);
pr(": Not a text file ********\n\n");
fclose (f);
return (NULL);
default:
break;
}
if (c == '\f') {
c = 0;
doclear ();
}
ungetc (c, f);
return (f);
}
/*
** A real function, for the tputs routine in termlib
*/
putch (ch)
register char ch;
{
putchar (ch);
}
/*
** Print out the contents of the file f, one screenful at a time.
*/
#define STOP -10
screen (f, num_lines)
register FILE *f;
register int num_lines;
{
register int c;
int nchars;
for (;;) {
while (num_lines > 0 && !pause) {
if ((nchars = getline (f)) == EOF)
return;
if (Senter && *Senter == ' ' && promptlen > 0)
erase (0);
pr (Line);
if (nchars < promptlen)
erase (nchars); /* erase () sets promptlen to 0 */
else promptlen = 0;
if (nchars < Mcol)
putchar('\n');
if (nchars == STOP)
break;
num_lines--;
}
fflush(stdout);
if ((c = getc(f)) == EOF) {
if (noscroll)
doclear();
else
erase (0);
return;
}
ungetc (c, f);
setjmp (restore);
pause = 0; startup = 0;
if ((num_lines = command (NULL, f)) == 0)
return;
}
}
/*
** Come here if a quit signal is received
*/
onquit()
{
signal(SIGQUIT, SIG_IGN);
if (!inwait) {
putchar ('\n');
if (!startup) {
signal(SIGQUIT, onquit);
longjmp (restore, 1);
}
else
pause++;
}
else if (!dum_opt && notell) {
write (2, "[Use q or Q to quit]", 20);
promptlen += 20;
notell = 0;
}
signal(SIGQUIT, onquit);
}
/*
** Clean up terminal state and exit. Also come here if interrupt signal received
*/
end_it ()
{
otty.sg_flags &= ~MBIT;
otty.sg_flags |= ECHO;
stty(2, &otty);
if (promptlen > 0)
kill_line ();
else
putchar ('\n');
exit(0);
}
copy_file(f)
register FILE *f;
{
register int c;
while ((c = getc(f)) != EOF)
putchar(c);
}
printd (n)
register int n;
{
register int a;
if (a = n/10)
printd(a);
putchar(n % 10 + '0');
}
static char bell = ctrl(G);
strlen (s)
char *s;
{
register char *p;
p = s;
while (*p++)
;
return (p - s - 1);
}
prompt (filename)
char *filename;
{
if (promptlen > 0)
kill_line ();
if (!hard) {
promptlen = 8;
if (Senter && Sexit)
tputs (Senter, 1, putch);
pr("--More--");
if (filename != NULL) {
pr("(Next file: ");
pr(filename);
putchar(')');
promptlen += 13 + strlen(filename);
}
if (dum_opt) {
pr("[Hit space to continue, Rubout to abort]");
promptlen += 40;
}
if (Senter && Sexit)
tputs (Sexit, 1, putch);
fflush(stdout);
}
else
write (2, &bell, 1);
inwait++;
}
/*
** Get a logical line
*/
getline(f)
register FILE *f;
{
register char c;
register char *p;
register int column;
register int i;
static int colflg;
p = Line;
i = column = 0;
c = getc (f);
if (colflg && c == '\n') c = getc (f);
for (i = 1; i < LINSIZ; i++) {
if (c == EOF) {
if (p > Line) {
*p = '\0';
return (column);
}
return (EOF);
}
if (c == '\n')
break;
*p++ = c;
if (c == '\t')
if (hardtabs && column < promptlen && !hard) {
if (eraseln && !dumb) {
tputs (eraseln, 1, putch);
promptlen = 0;
}
else {
for (--p; column & 7; column++)
*p++ = ' ';
if (column >= promptlen) promptlen = 0;
}
}
else
column = 1 + (column | 7);
else if (c == '\b')
column--;
else if (c == '\r')
column = 0;
else if (c == '\f' && stop_opt) {
p[-1] = '^';
*p++ = 'L';
break;
}
else if (c == EOF)
return (column);
else if (c >= ' ')
column++;
if (column >= Mcol) break;
c = getc (f);
}
if (Mcol > 0 && column >= Mcol) {
if (!Wrap) {
*p++ = '\n';
i++;
}
}
colflg = (column == Mcol) || c == '\f';
*p = 0;
if (c == '\f' && stop_opt)
return (STOP);
return (column);
}
/*
** Erase the rest of the prompt, assuming we are starting column col.
*/
erase (col)
register int col;
{
if (hard || promptlen == 0)
return;
if (col == 0)
putchar ('\r');
if (!dumb && eraseln)
tputs (eraseln, 1, putch);
else
for (col = promptlen - col; col > 0; col--)
putchar (' ');
promptlen = 0;
}
/*
** Erase the current line entirely
*/
kill_line ()
{
erase (0);
if (!eraseln || dumb) putchar ('\r');
}
/*
** Print string
*/
pr(s1)
char *s1;
{
register char *s;
register char c;
for (s = s1; c = *s++; )
putchar(c);
}
/*
** Clear the screen
*/
doclear()
{
if (Clear && Lpp > 0)
tputs(Clear, 1, putch);
}
/*
** Read a command and do it. A command consists of an optional integer
** argument followed by the command character. Return the number of lines
** to display in the next screenful. If there is nothing more to display
** in the current file, zero is returned.
*/
command (filename, f)
char *filename;
register FILE *f;
{
register int nlines;
register int retval;
register char c;
int id, done;
char comchar, cmdbuf[80], *p;
#define ret(val) retval=val;done++;break
done = 0;
if (!errors)
prompt (filename);
else
errors = 0;
if (MBIT == RAW && slow_tty) {
otty.sg_flags |= MBIT;
stty(2, &otty);
}
for (;;) {
nlines = number (&comchar);
switch (comchar) {
case ' ':
case 'z':
if (nlines == 0) nlines = dlines;
else if (comchar == 'z') dlines = nlines;
ret (nlines);
case 'd':
case ctrl(D):
ret (11);
case RUBOUT:
case 'q':
case 'Q':
end_it ();
case 's':
case 'f':
if (nlines == 0) nlines++;
if (comchar == 'f')
nlines *= dlines;
putchar ('\r');
erase (0);
pr("\n...skipping ");
printd(nlines);
pr(" line");
if (nlines > 1)
pr("s\n\n");
else
pr("\n\n");
while (nlines > 0) {
while ((c = getc (f)) != '\n')
if (c == EOF) {
retval = 0;
done++;
goto endsw;
}
nlines--;
}
ret (dlines);
break;
case '\n':
ret (1);
case 'n':
if (nlines == 0)
nlines++;
putchar ('\r');
erase (0);
skipf (nlines);
ret (0);
case 'p':
if (no_intty) {
write (2, &bell, 1);
break;
}
putchar ('\r');
erase (0);
if (nlines == 0)
nlines++;
skipf (-nlines);
ret (0);
case '/':
kill_line ();
pr ("/");
promptlen = 1;
fflush (stdout);
ttyin (cmdbuf, 78, '/');
if (nlines == 0) nlines++;
write (2, "\r", 1);
search (cmdbuf, f, nlines);
ret (dlines);
case '!':
kill_line ();
pr ("!");
promptlen = 1;
fflush (stdout);
ttyin (cmdbuf, 78, '!');
write (2, "\n", 1);
promptlen = 0;
otty.sg_flags |= ECHO;
otty.sg_flags &= ~MBIT;
stty(2, &otty);
while ((id = fork ()) < 0)
;
if (id == 0) {
execl (shell, shell, "-c", cmdbuf, 0);
write (2, "exec failed\n", 12);
exit (1);
}
signal (SIGINT, SIG_IGN);
signal (SIGQUIT, SIG_IGN);
wait (0);
signal (SIGINT, end_it);
signal (SIGQUIT, onquit);
otty.sg_flags |= MBIT;
otty.sg_flags &= ~ECHO;
stty(2, &otty);
pr ("----------\n(continue)\n");
fflush (stdout);
break;
default:
write (2, &bell, 1);
break;
}
if (done) break;
}
putchar ('\r');
endsw:
inwait = 0;
notell++;
if (MBIT == RAW && slow_tty) {
otty.sg_flags &= ~MBIT;
stty(2, &otty);
}
return (retval);
}
char ch;
/*
** Read a decimal number from the terminal. Set cmd to the non-digit which
** terminates the number.
*/
number(cmd)
char *cmd;
{
register int i;
i = 0; ch = otty.sg_kill;
for (;;) {
read (2, &ch, 1);
if (ch >= '0' && ch <= '9')
i = i*10 + ch - '0';
else if (ch == otty.sg_kill)
i = 0;
else {
*cmd = ch;
break;
}
}
return (i);
}
/*
** Skip n lines in the file f
*/
skiplns (n, f)
register int n;
register FILE *f;
{
register char c;
while (n > 0) {
while ((c = getc (f)) != '\n')
if (c == EOF)
return;
n--;
}
}
/*
** Skip nskip files in the file list (from the command line). Nskip may be
** negative.
*/
skipf (nskip)
register int nskip;
{
if (nskip == 0) return;
if (nskip > 0) {
if (fnum > nfiles - 1)
end_it ();
}
else if (within)
++fnum;
fnum += nskip;
if (fnum < 0)
fnum = 0;
else if (fnum > nfiles - 1)
fnum = nfiles -1;
pr ("\n...Skipping ");
pr (nskip > 0 ? "to file " : "back to file ");
pr (fnames[fnum]);
pr ("\n\n");
--fnum;
}
readch ()
{
char ch;
read (2, &ch, 1);
return (ch);
}
static char BS = '\b';
static char CARAT = '^';
ttyin (buf, nmax, pchar)
char buf[];
register int nmax;
char pchar;
{
register char *sptr;
register char ch;
register int slash = 0;
int maxlen;
char cbuf;
sptr = buf;
maxlen = 0;
while (sptr - buf < nmax) {
if (promptlen > maxlen) maxlen = promptlen;
ch = readch ();
if (ch == '\\') {
slash++;
}
else if (ch == otty.sg_erase && !slash) {
if (sptr > buf) {
--promptlen;
write (2, &BS, 1);
--sptr;
if (*sptr < ' ' && *sptr != '\n') {
--promptlen;
write (2, &BS, 1);
}
continue;
}
else {
if (!eraseln) promptlen = maxlen;
longjmp (restore, 1);
}
}
else if (ch == otty.sg_kill && !slash) {
if (hard)
pr (" XXX\n");
else {
putchar ('\r');
putchar (pchar);
if (eraseln)
erase (1);
promptlen = 1;
sptr = buf;
}
fflush (stdout);
continue;
}
if (slash && (ch == otty.sg_kill || ch == otty.sg_erase)) {
write (2, &BS, 1);
--sptr;
}
if (ch != '\\')
slash = 0;
*sptr++ = ch;
if (ch < ' ' && ch != '\n' && ch != ESC) {
ch += 0100;
write (2, &CARAT, 1);
promptlen++;
}
cbuf = ch;
if (ch != '\n' && ch != ESC) {
write (2, &cbuf, 1);
promptlen++;
}
else break;
}
*--sptr = '\0';
if (!eraseln) promptlen = maxlen;
if (sptr - buf >= nmax - 1)
error ("Line too long");
}
/*
** Search for nth ocurrence of regular expression contained in buf in the file
*/
search (buf, file, n)
char buf[];
FILE *file;
register int n;
{
long startline = ftell (file);
register long line1 = startline;
register long line2 = startline;
register long line3 = startline;
register int lncount;
lncount = 0;
compile (buf);
while (!feof (file)) {
line3 = line2;
line2 = line1;
line1 = ftell (file);
rdline (file);
lncount++;
if (execute (Line))
if (--n == 0) {
if (lncount > 3 || (lncount > 1 && no_intty))
pr ("\n...skipping\n");
if (!no_intty)
fseek (file, line3, 0);
else {
kill_line ();
pr (Line);
putchar ('\n');
}
break;
}
}
if (feof (file)) {
if (!no_intty) {
#ifdef CORY
file->_flag &= ~_IOEOF; /* why doesn't fseek do this ??!!??! */
#endif
fseek (file, startline, 0);
}
else {
pr ("\nPattern not found\n");
end_it ();
}
error ("Pattern not found");
}
}
/*
* The following are adapted from the editor
*/
/*
* Internal form of regular expressions.
*/
#define CBRA 1 /* left \( bracket */
#define CCHR 2 /* a particular character */
#define CDOT 4 /* any char (.) */
#define CCL 6 /* begin class ([) */
#define NCCL 8 /* begin not class ([^) */
#define CDOL 10 /* end of line ($) */
#define CEOF 11 /* end of pattern */
#define CKET 12 /* right \) bracket */
#define CBACK 14 /* repeat previous match (\1, etc on lhs) */
#define STAR 01 /* or'ed with some symbols to indicate * suffix */
#define NBRA 5 /* max # of \( \) pairs */
char expbuf[BUFSIZ];
char *braslist[NBRA];
char *braelist[NBRA];
int nbra;
int circfl;
char *loc1;
char *loc2;
char *locs;
/*
* compile: convert typed in regular expression into internal form.
* eof is the char that delimits the r.e.
* General structure of compiled r.e. in expbuf: A sequence of codes
* from #defines above (CCHR, CDOT, etc). Some of these take arguments
* which follow in line (e.g. CCHR is followed by the particular character
* it is required to match.) CEOF terminates the r.e.
*/
compile(inbuf)
char inbuf[];
{
register char c;
register char *ep;
register char *bp = inbuf;
char *lastep;
char bracket[NBRA], *bracketp;
int cclcnt;
/* comerr: compilation error. Don't leave half baked r.e. around. */
#define comerr(msg) {expbuf[0] = 0; nbra = 0; error(msg); }
ep = expbuf;
bracketp = bracket;
if ((c = *bp++) == '\0') {
/* null r.e.: just re-use last r.e., which is still there */
if (*ep==0)
error("No previous regular expression");
return;
}
nbra = 0;
/* circfl: true if have ^ (anchored search). */
circfl = 0;
if (c == '^') {
c = *bp++;
circfl++;
}
lastep = 0;
--bp;
for (;;) { /* for each character in the r.e. */
if (ep >= &expbuf[BUFSIZ])
comerr("r.e. too long");
c = *bp++;
if (c == '\0') {
/* Hit trailing delim: clean up and quit */
if (bracketp != bracket)
comerr("unmatched \\(");
*ep++ = CEOF;
*ep++ = 0;
return;
}
if (c!='*')
lastep = ep;
switch (c) {
case '\\':
if ((c = *bp++)=='(') {
/* \(: start of subexpression */
if (nbra >= NBRA)
comerr("too many \\(\\) pairs");
*bracketp++ = nbra;
*ep++ = CBRA;
*ep++ = nbra++;
continue;
}
if (c == ')') {
/* \): end of sub exp */
if (bracketp <= bracket)
comerr("unmatched \\)");
*ep++ = CKET;
*ep++ = *--bracketp;
continue;
}
if (c>='1' && c<'1'+NBRA) {
/* \1, \2, ...: rematch previous subexp */
*ep++ = CBACK;
*ep++ = c-'1';
continue;
}
/* Otherwise just force that char, not specially */
*ep++ = CCHR;
if (c=='\n')
/* Newlines can't possibly be in lines */
comerr("multi line r.e. not allowed");
*ep++ = c;
continue;
case '.':
/* .: match any character */
*ep++ = CDOT;
continue;
case '*':
/* *: Repeat last char indefinitely */
if (lastep==0 || *lastep==CBRA || *lastep==CKET)
/* Not that smart, so treat * as nonspecial */
goto defchar;
*lastep |= STAR;
continue;
case '$':
/* $: match end of line */
if (*bp != '\0')
/* $ only special at end of r.e. */
goto defchar;
*ep++ = CDOL;
continue;
case '[':
/*
* [...]: any of chars enclosed in brackets.
* Compiled form: CCL or NCCL, # of possible chars,
* then each char. -'s are expanded.
*/
*ep++ = CCL;
*ep++ = 0;
cclcnt = 1;
if ((c = *bp++) == '^') {
/* [^...]: reverse sense of match */
c = *bp++;
ep[-2] = NCCL;
}
do { /* for each char in brackets */
if (c=='\n')
comerr("missing ]");
if (c == '-' && ep[-1] != 0) {
/* form ...a-z... but [- not special */
if ((c = *bp++) == ']') {
/* -] not special either */
*ep++ = '-';
cclcnt++;
break;
}
while (ep[-1]<c) {
/* insert all chars between */
*ep = ep[-1]+1;
ep++;
cclcnt++;
if (ep>=&expbuf[BUFSIZ])
comerr("Too long");
}
}
*ep++ = c;
cclcnt++;
if (ep >= &expbuf[BUFSIZ])
comerr("Too long");
} while ((c = *bp++) != ']');
lastep[1] = cclcnt; /* backpatch count */
continue;
defchar:
default:
/*
* An ordinary char or one treated as ordinary.
* Store CCHR followed by that char, rather than
* just the char. This causes most r.e.'s to take
* up about twice the space you would expect.
* On the other hand, it makes r.e.'s beautifully
* portable, even though the codes could be real
* characters.
*/
*ep++ = CCHR;
*ep++ = c;
}
}
}
/*
* execute: look for the compiled r.e. on line addr.
* gf is 0 if this is the first time on this line, otherwise nonzero.
* If not first, start looking at locs, otherwise at beg of linebuf.
* loc1 and loc2 are set to the ends of the pattern found, if any.
* 1 is returned if successful, otherwise 0.
*/
execute(lptr)
char *lptr;
{
register char *p1, *p2;
register int c;
for (c=0; c<NBRA; c++) {
braslist[c] = 0;
braelist[c] = 0;
}
p1 = lptr;
p2 = expbuf;
if (circfl) {
/* anchored search (^): just try one advance. */
loc1 = p1;
return(advance(p1, p2));
}
/* fast check for first character */
if (*p2==CCHR) {
c = p2[1];
do {
if (*p1!=c)
continue;
if (advance(p1, p2)) {
loc1 = p1;
return(1);
}
} while (*p1++);
return(0);
}
/* regular algorithm, try advance starting at each char position. */
do {
if (advance(p1, p2)) {
loc1 = p1;
return(1);
}
} while (*p1++);
return(0);
}
/*
* advance: does an anchored search for expression starting at ep,
* looking in line starting at lp. Returns 1 if matches, else 0.
* If found, loc2 is set to end of pattern.
*/
advance(lp, ep)
register char *ep, *lp;
{
register char *curlp;
int i;
for (;;) switch (*ep++) { /* for each code in r.e., look at it..*/
case CCHR:
if (*ep++ == *lp++)
continue;
return(0);
case CDOT:
if (*lp++)
continue;
return(0);
case CDOL:
if (*lp==0)
continue;
return(0);
case CEOF:
loc2 = lp;
return(1);
case CCL:
if (cclass(ep, *lp++, 1)) {
ep += *ep;
continue;
}
return(0);
case NCCL:
if (cclass(ep, *lp++, 0)) {
ep += *ep;
continue;
}
return(0);
case CBRA:
braslist[*ep++] = lp;
continue;
case CKET:
braelist[*ep++] = lp;
continue;
case CBACK:
if (braelist[i = *ep++]==0)
error("bad back reference");
if (backref(i, lp)) {
lp += braelist[i] - braslist[i];
continue;
}
return(0);
case CBACK|STAR:
if (braelist[i = *ep++] == 0)
error("bad back reference");
curlp = lp;
while (backref(i, lp))
lp += braelist[i] - braslist[i];
while (lp >= curlp) {
if (advance(lp, ep))
return(1);
lp -= braelist[i] - braslist[i];
}
continue;
case CDOT|STAR:
curlp = lp;
while (*lp++)
;
goto star;
case CCHR|STAR:
curlp = lp;
while (*lp++ == *ep)
;
ep++;
goto star;
case CCL|STAR:
case NCCL|STAR:
curlp = lp;
while (cclass(ep, *lp++, ep[-1]==(CCL|STAR)))
;
ep += *ep;
goto star;
star:
/*
* star: special treatment. We have found as many of them
* as there are to find. Maybe this was too many, as dictated
* by what follows in the pattern. Try, starting from the
* end, to recursively advance after each char found,
* and return after first successful advance (thus finding
* largest possible string that matches).
*/
do {
lp--;
if (lp==locs)
break;
if (advance(lp, ep))
return(1);
} while (lp > curlp);
/* star failed at all attempts, so whole pattern fails. */
return(0);
default:
longjmp (restore, 1);
}
}
/*
* backref: checks to see that text starting at lp matches previous
* sub-expression #i. Returns 1 if successful, else 0. (Used for \k
* on lhs.)
*/
backref(i, lp)
register int i;
register char *lp;
{
register char *bp;
bp = braslist[i];
while (*bp++ == *lp++)
if (bp >= braelist[i])
return(1);
return(0);
}
/*
* cclass: check to see if character c is in class starting at set.
* ([...] construction on lhs of r.e.) af is sense of success/failure:
* af=1 is normal (success returns 1), af=0 is reversed for [^ (success
* returns 0).
*/
int
cclass(set, c, af)
register char *set, c;
int af;
{
register n;
if (c==0)
return(0);
n = *set++;
while (--n)
if (*set++ == c)
return(af);
return(!af);
}
error (mess)
char *mess;
{
if (promptlen > 0)
if (hard)
putchar ('\n');
else
kill_line ();
promptlen += strlen (mess);
if (Senter && Sexit) {
tputs (Senter, 1, putch);
pr(mess);
tputs (Sexit, 1, putch);
}
else
pr (mess);
if (hard)
putchar ('\n');
fflush(stdout);
errors++;
longjmp (restore, 1);
}
rdline (f)
register FILE *f;
{
register char c;
register char *p;
p = Line;
while ((c = getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1)
*p++ = c;
*p = '\0';
}