rsalz@bbn.com (Rich Salz) (04/26/88)
Submitted-by: Jonathan Payne <jpayne@cs.rochester.edu> Posting-number: Volume 14, Issue 66 Archive-name: jove4.9/part10 #! /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". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 10 (of 21)." PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f './extend.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'./extend.c'\" else echo shar: Extracting \"'./extend.c'\" \(19914 characters\) sed "s/^X//" >'./extend.c' <<'END_OF_FILE' X/*************************************************************************** X * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne. JOVE * X * is provided to you without charge, and with no warranty. You may give * X * away copies of JOVE, including sources, provided that this notice is * X * included in all the files. * X ***************************************************************************/ X X#include "jove.h" X#include "io.h" X#include "termcap.h" X#include "ctype.h" X#ifdef JOB_CONTROL X# include <signal.h> X#endif X X#ifdef MAC X# include "mac.h" X#else X# include <varargs.h> X#endif X X#ifdef MSDOS X#include <process.h> X#endif X X#ifdef MAC X# undef private X# define private X#endif X X#ifdef LINT_ARGS private void X fb_aux(data_obj *, data_obj **, char *, char *), X find_binds(data_obj *, char *), X vpr_aux(struct variable *, char *); X#else private void X fb_aux(), X find_binds(), X vpr_aux(); X#endif /* LINT_ARGS */ X X#ifdef MAC X# undef private X# define private static X#endif X X int InJoverc = 0; X extern int getch(), X getchar(); X X/* Auto execute code */ X X#define NEXECS 20 X private struct { X char *a_pattern; X data_obj *a_cmd; X} AutoExecs[NEXECS] = {0}; X private int ExecIndex = 0; X X/* Command auto-execute. */ X void CAutoExec() X{ X DefAutoExec(findcom); X} X X/* Macro auto-execute. */ X void MAutoExec() X{ X DefAutoExec(findmac); X} X X/* VARARGS0 */ X void DefAutoExec(proc) X#ifdef LINT_ARGS data_obj *(*proc)(); X#else data_obj *(*proc)(); X#endif X{ X data_obj *d; X char *pattern; X int i; X X if (ExecIndex >= NEXECS) X complain("Too many auto-executes, max %d.", NEXECS); X if ((d = (*proc)(ProcFmt)) == 0) X return; X pattern = do_ask("\r\n", (int (*)()) 0, (char *) 0, ": %f %s ", d->Name); X if (pattern != 0) X for (i = 0; i < ExecIndex; i++) X if ((AutoExecs[i].a_cmd == d) && X (strcmp(pattern, AutoExecs[i].a_pattern) == 0)) X return; /* eliminate duplicates */ X AutoExecs[ExecIndex].a_pattern = copystr(pattern); X AutoExecs[ExecIndex].a_cmd = d; X ExecIndex += 1; X} X X/* DoAutoExec: NEW and OLD are file names, and if NEW and OLD aren't the X same kind of file (i.e., match the same pattern) or OLD is 0 and it X matches, OR if the pattern is 0 (none was specified) then, we execute X the command associated with that kind of file. */ X void DoAutoExec(new, old) register char *new, X *old; X{ X register int i; X X set_arg_value(1); X for (i = 0; i < ExecIndex; i++) X if ((AutoExecs[i].a_pattern == 0) || X ((new != 0 && LookingAt(AutoExecs[i].a_pattern, new, 0)) && X (old == 0 || !LookingAt(AutoExecs[i].a_pattern, old, 0)))) X ExecCmd(AutoExecs[i].a_cmd); X} X void BindAKey() X{ X BindSomething(findcom); X} X void BindMac() X{ X BindSomething(findmac); X} X extern void EscPrefix(), X CtlxPrefix(), X MiscPrefix(); X data_obj ** IsPrefix(cp) data_obj *cp; X{ X#ifdef MAC X void (*proc)(); X#else X int (*proc)(); X#endif X X if (cp == 0 || (cp->Type & TYPEMASK) != FUNCTION) X return 0; X proc = ((struct cmd *) cp)->c_proc; X if (proc == EscPrefix) X return pref1map; X if (proc == CtlxPrefix) X return pref2map; X if (proc == MiscPrefix) X return miscmap; X return 0; X} X void UnbindC() X{ X char *keys; X data_obj **map = mainmap; X X keys = ask((char *) 0, ProcFmt); X for (;;) { X if (keys[1] == '\0') X break; X if ((map = IsPrefix(map[*keys])) == 0) X break; X keys += 1; X } X if (keys[1] != 0) X complain("That's not a legitimate key sequence."); X map[keys[0]] = 0; X} X int addgetc() X{ X int c; X X if (!InJoverc) { X Asking = strlen(mesgbuf); X c = getch(); X Asking = 0; X add_mess("%p ", c); X } else { X c = getch(); X if (c == '\n') X return EOF; /* this isn't part of the sequence */ X else if (c == '\\') { X if ((c = getch()) == LF) X complain("[Premature end of line]"); X } else if (c == '^') { X if ((c = getch()) == '?') X c = RUBOUT; X else if (isalpha(c) || index("@[\\]^_", c)) X c = CTL(c); X else X complain("[Unknown control character]"); X } X } X return c; X} X void BindWMap(map, lastkey, cmd) data_obj **map, X *cmd; X{ X data_obj **nextmap; X int c; X X c = addgetc(); X if (c == EOF) { X if (lastkey == EOF) X complain("[Empty key sequence]"); X complain("[Premature end of key sequence]"); X } else { X if (nextmap = IsPrefix(map[c])) X BindWMap(nextmap, c, cmd); X else { X map[c] = cmd; X#ifdef MAC X ((struct cmd *) cmd)->c_key = c; /* see about_j() in mac.c */ X if(map == mainmap) ((struct cmd *) cmd)->c_map = F_MAINMAP; X else if(map == pref1map) ((struct cmd *) cmd)->c_map = F_PREF1MAP; X else if(map == pref2map) ((struct cmd *) cmd)->c_map = F_PREF2MAP; X#endif X } X } X} X X/* VARARGS0 */ X void BindSomething(proc) X#ifdef LINT_ARGS data_obj *(*proc)(); X#else data_obj *(*proc)(); X#endif X{ X data_obj *d; X X if ((d = (*proc)(ProcFmt)) == 0) X return; X s_mess(": %f %s ", d->Name); X BindWMap(mainmap, EOF, d); X} X X/* Describe key */ X void DescWMap(map, key) data_obj **map; X{ X data_obj *cp = map[key], X **prefp; X X if (cp == 0) X add_mess("is unbound."); X else if (prefp = IsPrefix(cp)) X DescWMap(prefp, addgetc()); X else X add_mess("is bound to %s.", cp->Name); X} X void KeyDesc() X{ X s_mess(ProcFmt); X DescWMap(mainmap, addgetc()); X} X void DescCom() X{ X data_obj *dp; X char pattern[100], X doc_type[40], X *file = CmdDb; X File *fp; X X if (!strcmp(LastCmd->Name, "describe-variable")) X dp = (data_obj *) findvar(ProcFmt); X else X dp = (data_obj *) findcom(ProcFmt); X if (dp == 0) X return; X fp = open_file(file, iobuff, F_READ, COMPLAIN, QUIET); X Placur(ILI, 0); X flusho(); X sprintf(pattern, "^:entry \"%s\" \"\\([^\"]*\\)\"", dp->Name); X TOstart("Help", TRUE); X for (;;) { X if (f_gets(fp, genbuf, LBSIZE) == EOF) { X Typeout("There is no documentation for \"%s\".", dp->Name); X goto outahere; X } X if ((strncmp(genbuf, ":entry", 6) == 0) && LookingAt(pattern, genbuf, 0)) X break; X } X /* found it ... let's print it */ X putmatch(1, doc_type, sizeof doc_type); X if (strcmp("Variable", doc_type) == 0) X Typeout(dp->Name); X else if (strcmp("Command", doc_type) == 0) { X char binding[128]; X X find_binds(dp, binding); X if (blnkp(binding)) X Typeout("To invoke %s, type \"ESC X %s<cr>\".", X dp->Name, X dp->Name); X else X Typeout("Type \"%s\" to invoke %s.", binding, dp->Name); X } X Typeout(""); X while (f_gets(fp, genbuf, LBSIZE) != EOF) X if (strncmp(genbuf, ":entry", 6) == 0) X goto outahere; X else X Typeout("%s", genbuf); outahere: X f_close(fp); X TOstop(); X} X void DescBindings() X{ X extern void Typeout(); X X TOstart("Key Bindings", TRUE); X DescMap(mainmap, NullStr); X TOstop(); X} X extern int specialmap; X void DescMap(map, pref) data_obj **map; char *pref; X{ X int c1, X c2 = 0, X numbetween; X char keydescbuf[40]; X data_obj **prefp; X X#ifdef IBMPC X specialmap = (map == miscmap); X#endif X X for (c1 = 0; c1 < NCHARS && c2 < NCHARS; c1 = c2 + 1) { X c2 = c1; X if (map[c1] == 0) X continue; X while (++c2 < NCHARS && map[c1] == map[c2]) X ; X c2 -= 1; X numbetween = c2 - c1; X if (numbetween == 1) X sprintf(keydescbuf, "%s {%p,%p}", pref, c1, c2); X else if (numbetween == 0) X sprintf(keydescbuf, "%s %p", pref, c1); X else X sprintf(keydescbuf, "%s [%p-%p]", pref, c1, c2); X if ((prefp = IsPrefix(map[c1])) && (prefp != map)) X DescMap(prefp, keydescbuf); X else X Typeout("%-18s%s", keydescbuf, map[c1]->Name); X } X} X private void find_binds(dp, buf) data_obj *dp; char *buf; X{ X char *endp; X X buf[0] = '\0'; X fb_aux(dp, mainmap, (char *) 0, buf); X endp = buf + strlen(buf) - 2; X if ((endp > buf) && (strcmp(endp, ", ") == 0)) X *endp = '\0'; X} X private void fb_aux(cp, map, prefix, buf) register data_obj *cp, X **map; char *buf, X *prefix; X{ X int c1, X c2; X char *bufp = buf + strlen(buf), X prefbuf[20]; X data_obj **prefp; X X#ifdef IBMPC X specialmap = (map == miscmap); X#endif X X for (c1 = c2 = 0; c1 < NCHARS && c2 < NCHARS; c1 = c2 + 1) { X c2 = c1; X if (map[c1] == cp) { X while (++c2 < NCHARS && map[c1] == map[c2]) X ; X c2 -= 1; X if (prefix) X sprintf(bufp, "%s ", prefix); X bufp += strlen(bufp); X switch (c2 - c1) { X case 0: X sprintf(bufp, "%p, ", c1); X break; X X case 1: X sprintf(bufp, "{%p,%p}, ", c1, c2); X break; X X default: X sprintf(bufp, "[%p-%p], ", c1, c2); X break; X } X } X if ((prefp = IsPrefix(map[c1])) && (prefp != map)) { X sprintf(prefbuf, "%p", c1); X fb_aux(cp, prefp, prefbuf, bufp); X } X bufp += strlen(bufp); X } X} X void Apropos() X{ X register struct cmd *cp; X register struct macro *m; X register struct variable *v; X char *ans; X int anyfs = NO, X anyvs = NO, X anyms = NO; X char buf[256]; X X ans = ask((char *) 0, ": %f (keyword) "); X TOstart("Help", TRUE); X for (cp = commands; cp->Name != 0; cp++) X if (sindex(ans, cp->Name)) { X if (anyfs == 0) { X Typeout("Commands"); X Typeout("--------"); X } X find_binds((data_obj *) cp, buf); X if (buf[0]) X Typeout(": %-35s(%s)", cp->Name, buf); X else X Typeout(": %s", cp->Name); X anyfs = YES; X } X if (anyfs) X Typeout(NullStr); X for (v = variables; v->Name != 0; v++) X if (sindex(ans, v->Name)) { X if (anyvs == 0) { X Typeout("Variables"); X Typeout("---------"); X } X anyvs = YES; X vpr_aux(v, buf); X Typeout(": set %-26s%s", v->Name, buf); X } X if (anyvs) X Typeout(NullStr); X for (m = macros; m != 0; m = m->m_nextm) X if (sindex(ans, m->Name)) { X if (anyms == 0) { X Typeout("Macros"); X Typeout("------"); X } X anyms = YES; X find_binds((data_obj *) m, buf); X if (buf[0]) X Typeout(": %-35s(%s)", m->Name, buf); X else X Typeout(": %-35s%s", "execute-macro", m->Name); X } X TOstop(); X} X void Extend() X{ X data_obj *d; X X if (d = findcom(": ")) X ExecCmd(d); X} X X/* Read a positive integer from CP. It must be in base BASE, and X complains if it isn't. If allints is nonzero, all the characters X in the string must be integers or we return -1; otherwise we stop X reading at the first nondigit. */ X int chr_to_int(cp, base, allints, result) register char *cp; register int *result; X{ X register int c; X int value = 0, X sign; X X if ((c = *cp) == '-') { X sign = -1; X cp += 1; X } else X sign = 1; X while (c = *cp++) { X if (!isdigit(c)) { X if (allints == YES) X return INT_BAD; X break; X } X c = c - '0'; X if (c >= base) X complain("You must specify in base %d.", base); X value = value * base + c; X } X *result = value * sign; X return INT_OKAY; X} X int ask_int(prompt, base) char *prompt; int base; X{ X char *val = ask((char *) 0, prompt); X int value; X X if (chr_to_int(val, base, YES, &value) == INT_BAD) X complain("That's not a number!"); X return value; X} X private void vpr_aux(vp, buf) register struct variable *vp; char *buf; X{ X switch (vp->v_flags & V_TYPEMASK) { X case V_BASE10: X sprintf(buf, "%d", *(vp->v_value)); X break; X X case V_BASE8: X sprintf(buf, "%o", *(vp->v_value)); X break; X X case V_BOOL: X sprintf(buf, (*(vp->v_value)) ? "on" : "off"); X break; X X case V_STRING: X case V_FILENAME: X sprintf(buf, "%s", (char *) vp->v_value); X break; X X case V_CHAR: X sprintf(buf, "%p", *(vp->v_value)); X break; X } X} X void PrVar() X{ X struct variable *vp; X char prbuf[256]; X X if ((vp = (struct variable *) findvar(ProcFmt)) == 0) X return; X vpr_aux(vp, prbuf); X s_mess(": %f %s => %s", vp->Name, prbuf); X} X void SetVar() X{ X struct variable *vp; X char *prompt; X X if ((vp = (struct variable *) findvar(ProcFmt)) == 0) X return; X prompt = sprint(": %f %s ", vp->Name); X X switch (vp->v_flags & V_TYPEMASK) { X case V_BASE10: X case V_BASE8: X { X int value; X X value = ask_int(prompt, ((vp->v_flags & V_TYPEMASK) == V_BASE10) X ? 10 : 8); X *(vp->v_value) = value; X break; X } X X case V_BOOL: X { X char *def = *(vp->v_value) ? "off" : "on", X *on_off; X int value; X X on_off = ask(def, prompt); X if (casecmp(on_off, "on") == 0) X value = ON; X else if (casecmp(on_off, "off") == 0) X value = OFF; X else X complain("Boolean variables must be ON or OFF."); X *(vp->v_value) = value; X#ifdef MAC X MarkVar(vp,-1,0); /* mark the menu item */ X#endif X s_mess("%s%s", prompt, value ? "on" : "off"); X break; X } X X case V_FILENAME: X { X char fbuf[FILESIZE]; X X sprintf(&prompt[strlen(prompt)], "(default %s) ", vp->v_value); X (void) ask_file(prompt, (char *) vp->v_value, fbuf); X strcpy((char *) vp->v_value, fbuf); X break; X } X X case V_STRING: X { X char *str; X X /* Do_ask() so you can set string to "" if you so desire. */ X str = do_ask("\r\n", (int (*)()) 0, (char *) vp->v_value, prompt); X if (str == 0) X str = NullStr; X strcpy((char *) vp->v_value, str); X /* ... and hope there is enough room. */ X break; X } X case V_CHAR: X f_mess(prompt); X *(vp->v_value) = addgetc(); X break; X X } X if (vp->v_flags & V_MODELINE) X UpdModLine = YES; X if (vp->v_flags & V_CLRSCREEN) { X#ifdef IBMPC X setcolor(Fgcolor, Bgcolor); X#endif /* IBMPC */ X ClAndRedraw(); X } X if (vp->v_flags & V_TTY_RESET) X tty_reset(); X} X X/* Command completion - possible is an array of strings, prompt is X the prompt to use, and flags are ... well read jove.h. X X If flags are RET_STATE, and the user hits <return> what they typed X so far is in the Minibuf string. */ X private char **Possible; private int comp_value, X comp_flags; X int aux_complete(c) X{ X int command, X length, X i; X X if (comp_flags & CASEIND) { X char *lp; X X for (lp = linebuf; *lp != '\0'; lp++) X#if (defined(IBMPC) || defined(MAC)) X lower(lp); X#else X if (isupper(*lp)) X *lp = tolower(*lp); X#endif X } X switch (c) { X case EOF: X comp_value = -1; X return 0; X X case '\r': X case '\n': X command = match(Possible, linebuf); X if (command >= 0) { X comp_value = command; X return 0; /* tells ask to stop */ X } X if (eolp() && bolp()) { X comp_value = NULLSTRING; X return 0; X } X if (comp_flags & RET_STATE) { X comp_value = command; X return 0; X } X if (InJoverc) X complain("[\"%s\" unknown]", linebuf); X rbell(); X break; X X case '\t': X case ' ': X { X int minmatch = 1000, X maxmatch = 0, X numfound = 0, X lastmatch = -1, X length = strlen(linebuf); X X for (i = 0; Possible[i] != 0; i++) { X int this_len; X X this_len = numcomp(Possible[i], linebuf); X maxmatch = max(maxmatch, this_len); X if (this_len >= length) { X if (numfound) X minmatch = min(minmatch, numcomp(Possible[lastmatch], Possible[i])); X else X minmatch = strlen(Possible[i]); X numfound += 1; X lastmatch = i; X if (strcmp(linebuf, Possible[i]) == 0) X break; X } X } X X if (numfound == 0) { X rbell(); X if (InJoverc) X complain("[\"%s\" unknown]", linebuf); X /* If we're not in the .joverc then X let's do something helpful for the X user. */ X if (maxmatch < length) { X char *cp; X X cp = linebuf + maxmatch; X *cp = 0; X Eol(); X } X break; X } X if (c != '\t' && numfound == 1) { X comp_value = lastmatch; X return 0; X } X null_ncpy(linebuf, Possible[lastmatch], minmatch); X Eol(); X if (minmatch == length) /* No difference */ X rbell(); X break; X } X X case '?': X if (InJoverc) X complain((char *) 0); X /* kludge: in case we're using UseBuffers, in which case X linebuf gets written all over */ X strcpy(Minibuf, linebuf); X length = strlen(Minibuf); X TOstart("Completion", TRUE); /* for now ... */ X for (i = 0; Possible[i]; i++) X if (numcomp(Possible[i], Minibuf) >= length) { X Typeout(Possible[i]); X if (TOabort != 0) X break; X } X X TOstop(); X break; X } X return !FALSE; X} X int complete(possible, prompt, flags) register char *possible[]; char *prompt; X{ X Possible = possible; X comp_flags = flags; X (void) do_ask("\r\n \t?", aux_complete, NullStr, prompt); X return comp_value; X} X int match(choices, what) register char **choices, X *what; X{ X register int len; X int i, X found = 0, X save, X exactmatch = -1; X X len = strlen(what); X if (len == 0) X return NULLSTRING; X for (i = 0; choices[i]; i++) { X if (strncmp(what, choices[i], len) == 0) { X if (strcmp(what, choices[i]) == 0) X exactmatch = i; X save = i; X found += 1; /* found one */ X } X } X X if (found == 0) X save = ORIGINAL; X else if (found > 1) { X if (exactmatch != -1) X save = exactmatch; X else X save = AMBIGUOUS; X } X X return save; X} X void Source() X{ X char *com, *getenv(), X buf[FILESIZE]; X X#ifndef MSDOS X sprintf(buf, "%s/.joverc", getenv("HOME")); X#else /* MSDOS */ X if (com = getenv("JOVERC")) X strcpy(buf, com); X else X strcpy(buf, Joverc); X#endif /* MSDOS */ X com = ask_file((char *) 0, buf, buf); X if (joverc(buf) == 0) X complain(IOerr("read", com)); X} X void BufPos() X{ X register Line *lp = curbuf->b_first; X register int i, X dotline; X long dotchar, X nchars; X X for (i = nchars = 0; lp != 0; i++, lp = lp->l_next) { X if (lp == curline) { X dotchar = nchars + curchar; X dotline = i + 1; X } X nchars += length(lp) + (lp->l_next != 0); /* include the NL */ X } X X s_mess("[\"%s\" line %d/%d, char %D/%D (%d%%), cursor = %d/%d]", X filename(curbuf), dotline, i, dotchar, nchars, X (nchars == 0) ? 100 : (int) (((long) dotchar * 100) / nchars), X calc_pos(linebuf, curchar), X calc_pos(linebuf, strlen(linebuf))); X} X X#define IF_UNBOUND -1 X#define IF_TRUE 1 X#define IF_FALSE !IF_TRUE X X#ifndef MAC int do_if(cmd) char *cmd; X{ X#ifdef MSDOS X int status; X#else X int pid, X status; X#endif /* MSDOS */ X#ifndef MSDOS X X switch (pid = fork()) { X case -1: X complain("[Fork failed: if]"); X X case 0: X { X#endif /* MSDOS */ X char *args[12], X *cp = cmd, X **ap = args; X X *ap++ = cmd; X for (;;) { X if ((cp = index(cp, ' ')) == 0) X break; X *cp++ = '\0'; X *ap++ = cp; X } X *ap = 0; X X#ifndef MSDOS X close(0); /* we want reads to fail */ X /* close(1); but not writes or ioctl's X close(2); */ X#else /* MSDOS */ X if ((status = spawnvp(0, args[0], args)) < 0) X complain("[Spawn failed: if]"); X#endif /* MSDOS */ X X#ifndef MSDOS X (void) execvp(args[0], args); X _exit(-10); /* signals exec error (see below) */ X } X } X#ifdef IPROCS X sighold(SIGCHLD); X#endif X dowait(pid, &status); X#ifdef IPROCS X sigrelse(SIGCHLD); X#endif X if (status == -10) X complain("[Exec failed]"); X if (status < 0) X complain("[Exit %d]", status); X#endif /* MSDOS */ X return (status == 0); /* 0 means successful */ X} X#endif /* MAC */ X int joverc(file) char *file; X{ X char buf[LBSIZE], X lbuf[LBSIZE]; X int lnum = 0, X eof = FALSE; X jmp_buf savejmp; X int IfStatus = IF_UNBOUND; X File *fp; X X fp = open_file(file, buf, F_READ, !COMPLAIN, QUIET); X if (fp == NIL) X return NO; /* joverc returns an integer */ X X /* Catch any errors, here, and do the right thing with them, X and then restore the error handle to whoever did a setjmp X last. */ X X InJoverc += 1; X push_env(savejmp); X if (setjmp(mainjmp)) { X Buffer *savebuf = curbuf; X X SetBuf(do_select((Window *) 0, "RC errors")); X ins_str(sprint("%s:%d:%s\t%s\n", pr_name(file, YES), lnum, lbuf, mesgbuf), NO); X unmodify(); X SetBuf(savebuf); X Asking = 0; X } X if (!eof) do { X eof = (f_gets(fp, lbuf, sizeof lbuf) == EOF); X lnum += 1; X if (lbuf[0] == '#') /* a comment */ X continue; X#ifndef MAC X if (casencmp(lbuf, "if", 2) == 0) { X char cmd[128]; X X if (IfStatus != IF_UNBOUND) X complain("[Cannot have nested if's]"); X if (LookingAt("if[ \t]*\\(.*\\)$", lbuf, 0) == 0) X complain("[If syntax error]"); X putmatch(1, cmd, sizeof cmd); X IfStatus = do_if(cmd) ? IF_TRUE : IF_FALSE; X continue; X } else if (casencmp(lbuf, "else", 4) == 0) { X if (IfStatus == IF_UNBOUND) X complain("[Unexpected `else']"); X IfStatus = !IfStatus; X continue; X } else if (casencmp(lbuf, "endif", 5) == 0) { X if (IfStatus == IF_UNBOUND) X complain("[Unexpected `endif']"); X IfStatus = IF_UNBOUND; X continue; X } X#endif X if (IfStatus == IF_FALSE) X continue; X (void) strcat(lbuf, "\n"); X Inputp = lbuf; X while (*Inputp == ' ' || *Inputp == '\t') X Inputp += 1; /* skip white space */ X Extend(); X } while (!eof); X X f_close(fp); X pop_env(savejmp); X Inputp = 0; X Asking = 0; X InJoverc -= 1; X if (IfStatus != IF_UNBOUND) X complain("[Missing endif]"); X return 1; X} END_OF_FILE if test 19914 -ne `wc -c <'./extend.c'`; then echo shar: \"'./extend.c'\" unpacked with wrong size! fi # end of './extend.c' fi if test -f './re.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'./re.c'\" else echo shar: Extracting \"'./re.c'\" \(18437 characters\) sed "s/^X//" >'./re.c' <<'END_OF_FILE' X/*************************************************************************** X * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne. JOVE * X * is provided to you without charge, and with no warranty. You may give * X * away copies of JOVE, including sources, provided that this notice is * X * included in all the files. * X ***************************************************************************/ X X/* search package */ X X#include "jove.h" X#include "ctype.h" X#ifdef MAC X# undef private X# define private X#endif X X#ifdef LINT_ARGS private char * insert(char *, char *, int); X private void X REreset(void), X search(int, int, int); private int X backref(int, char *), X do_comp(int), X member(char *, int, int), X REgetc(void), X REmatch(char *, char *); X#else private char * insert(); X private void X REreset(), X search(); private int X backref(), X do_comp(), X member(), X REgetc(), X REmatch(); X#endif /* LINT_ARGS */ X X#ifdef MAC X# undef private X# define private static X#endif X X#define NALTS 16 /* number of alternate search strings */ X char searchstr[128], X compbuf[256], /* global default compbuf */ X rep_search[128], /* replace search string */ X rep_str[128], /* contains replacement string */ X *cur_compb, /* usually points at compbuf */ X REbuf[LBSIZE], /* points at line we're scanning */ X *alternates[NALTS]; X int REdirection; X int CaseIgnore = 0, X WrapScan = 0, X UseRE = 0; X X#define cind_cmp(a, b) (CaseEquiv[a] == CaseEquiv[b]) X private int REpeekc; private char *REptr; X private int REgetc() X{ X int c; X X if ((c = REpeekc) != -1) X REpeekc = -1; X else if (*REptr) X c = *REptr++; X else X c = 0; X X return c; X} X X#define STAR 01 /* Match any number of last RE. */ X#define AT_BOL 2 /* ^ */ X#define AT_EOL 4 /* $ */ X#define AT_BOW 6 /* \< */ X#define AT_EOW 8 /* \> */ X#define OPENP 10 /* \( */ X#define CLOSEP 12 /* \) */ X#define CURLYB 14 /* \{ */ X X#define NOSTR 14 /* Codes <= NOSTR can't be *'d. */ X X#define ANYC NOSTR+2 /* . */ X#define NORMC ANYC+2 /* normal character */ X#define CINDC NORMC+2 /* case independent character */ X#define ONE_OF CINDC+2 /* [xxx] */ X#define NONE_OF ONE_OF+2 /* [^xxx] */ X#define BACKREF NONE_OF+2 /* \# */ X#define EOP BACKREF+2 /* end of pattern */ X X#define NPAR 10 /* [0-9] - 0th is the entire matched string, i.e. & */ private int nparens; private char *comp_p, X *start_p, X **alt_p, X **alt_endp; X void REcompile(pattern, re, into_buf, alt_bufp) char *pattern, X *into_buf, X **alt_bufp; X{ X REptr = pattern; X REpeekc = -1; X comp_p = cur_compb = start_p = into_buf; X alt_p = alt_bufp; X alt_endp = alt_p + NALTS; X *alt_p++ = comp_p; X nparens = 0; X (void) do_comp(re ? OKAY_RE : NORM); X *alt_p = 0; X} X X/* compile the pattern into an internal code */ X private int do_comp(kind) X{ X char *last_p, X *chr_cnt = 0; X int parens[NPAR], X *parenp, X c, X ret_code; X X parenp = parens; X last_p = 0; X ret_code = 1; X X if (kind == OKAY_RE) { X *comp_p++ = OPENP; X *comp_p++ = nparens; X *parenp++ = nparens++; X start_p = comp_p; X } X X while (c = REgetc()) { X if (comp_p > &cur_compb[(sizeof compbuf) - 6]) toolong: complain("Search string too long/complex."); X if (c != '*') X last_p = comp_p; X X if (kind == NORM && index(".[*", c) != 0) X goto defchar; X switch (c) { X case '\\': X switch (c = REgetc()) { X case 0: X complain("Premature end of pattern."); X X case '{': X { X char *wcntp; /* word count */ X X *comp_p++ = CURLYB; X wcntp = comp_p; X *comp_p++ = 0; X for (;;) { X int comp_val; X char *comp_len; X X comp_len = comp_p++; X comp_val = do_comp(IN_CB); X *comp_len = comp_p - comp_len; X (*wcntp) += 1; X if (comp_val == 0) X break; X } X break; X } X X case '}': X if (kind != IN_CB) X complain("Unexpected \}."); X ret_code = 0; X goto outahere; X X case '(': X if (nparens >= NPAR) X complain("Too many ('s; max is %d.", NPAR); X *comp_p++ = OPENP; X *comp_p++ = nparens; X *parenp++ = nparens++; X break; X X case ')': X if (parenp == parens) X complain("Too many )'s."); X *comp_p++ = CLOSEP; X *comp_p++ = *--parenp; X break; X X case '|': X if (alt_p >= alt_endp) X complain("Too many alternates; max %d.", NALTS); X *comp_p++ = CLOSEP; X *comp_p++ = *--parenp; X *comp_p++ = EOP; X *alt_p++ = comp_p; X nparens = 0; X *comp_p++ = OPENP; X *comp_p++ = nparens; X *parenp++ = nparens++; X start_p = comp_p; X break; X X case '1': X case '2': X case '3': X case '4': X case '5': X case '6': X case '7': X case '8': X case '9': X *comp_p++ = BACKREF; X *comp_p++ = c - '0'; X break; X X case '<': X *comp_p++ = AT_BOW; X break; X X case '>': X *comp_p++ = AT_EOW; X break; X X default: X goto defchar; X } X break; X X case ',': X if (kind != IN_CB) X goto defchar; X goto outahere; X X case '.': X *comp_p++ = ANYC; X break; X X case '^': X if (comp_p == start_p) { X *comp_p++ = AT_BOL; X break; X } X goto defchar; X X case '$': X if ((REpeekc = REgetc()) != 0 && REpeekc != '\\') X goto defchar; X *comp_p++ = AT_EOL; X break; X X case '[': X { X int chrcnt; X X *comp_p++ = ONE_OF; X if (comp_p + 16 >= &cur_compb[(sizeof compbuf)]) X goto toolong; X bzero(comp_p, 16); X if ((REpeekc = REgetc()) == '^') { X *last_p = NONE_OF; X /* Get it for real this time. */ X (void) REgetc(); X } X chrcnt = 1; X while ((c = REgetc()) != ']' && c != 0) { X if (c == '\\') X c = REgetc(); X else if ((REpeekc = REgetc()) == '-') { X int c2; X X (void) REgetc(); /* reread '-' */ X c2 = REgetc(); X while (c < c2) { X comp_p[c/8] |= (1 << (c%8)); X c += 1; X } X } X comp_p[c/8] |= (1 << (c%8)); X chrcnt += 1; X } X if (c == 0) X complain("Missing ]."); X if (chrcnt == 1) X complain("Empty []."); X comp_p += 16; X break; X } X X case '*': X if (last_p == 0 || *last_p <= NOSTR) X goto defchar; X X /* The * operator applies only to the previous X character. If we were building a chr_cnt at X the time we got the *, we have to remove the X last character from the chr_cnt (by decrementing X *chr_cnt) and replacing it with a new STAR entry. X X If we are decrementing the count to 0, we just X delete the chr_cnt entry altogether, replacing X it with the STAR entry. */ X X if (chr_cnt) { X char lastc = chr_cnt[*chr_cnt]; X X /* The * operator applies only to the previous X character. If we were building a chr_cnt at X the time we got the *, we have to remove the X last character from the chr_cnt (by decrementing X *chr_cnt) and replacing it with a new STAR entry. X X If we are decrementing the count to 0, we just X delete the chr_cnt entry altogether, replacing X it with the STAR entry. */ X X if (*chr_cnt == 1) { X comp_p = chr_cnt; X comp_p[-1] |= STAR; X *comp_p++ = lastc; X } else { X comp_p = chr_cnt + *chr_cnt; X (*chr_cnt) -= 1; X *comp_p++ = chr_cnt[-1] | STAR; X *comp_p++ = lastc; X } X } else X *last_p |= STAR; X break; X default: defchar: if (chr_cnt) X (*chr_cnt) += 1; X else { X *comp_p++ = (CaseIgnore) ? CINDC : NORMC; X chr_cnt = comp_p++; X *chr_cnt = 1; /* last_p[1] = 1; */ X } X *comp_p++ = c; X continue; X } X chr_cnt = FALSE; X } outahere: X /* End of pattern, let's do some error checking. */ X if (kind == OKAY_RE) { X *comp_p++ = CLOSEP; X *comp_p++ = *--parenp; X } X if (parenp != parens) X complain("Unmatched ()'s."); X if (kind == IN_CB && c == 0) /* End of pattern with \}. */ X complain("Missing \}."); X *comp_p++ = EOP; X X return ret_code; X} X private char *pstrtlst[NPAR], /* index into REbuf */ X *pendlst[NPAR], X *REbolp, X *locs, X *loc1, X *loc2; X int REbom, X REeom, /* beginning and end of match */ X REalt_num; /* if alternatives, which one matched? */ X private int backref(n, linep) register char *linep; X{ X register char *backsp, X *backep; X X backsp = pstrtlst[n]; X backep = pendlst[n]; X while (*backsp++ == *linep++) X if (backsp >= backep) X return 1; X return 0; X} X private int member(comp_p, c, af) register char *comp_p; register int c, X af; X{ X if (c == 0) X return 0; /* try to match EOL always fails */ X if (comp_p[c/8] & (1 << (c%8))) X return af; X return !af; X} X private int REmatch(linep, comp_p) register char *linep, X *comp_p; X{ X char *first_p = linep; X register int n; X X for (;;) switch (*comp_p++) { X case NORMC: X n = *comp_p++; X while (--n >= 0) X if (*linep++ != *comp_p++) X return 0; X continue; X X case CINDC: /* case independent comparison */ X n = *comp_p++; X while (--n >= 0) X if (!cind_cmp(*linep++, *comp_p++)) X return 0; X continue; X X case EOP: X loc2 = linep; X REeom = (loc2 - REbolp); X return 1; /* Success! */ X X case AT_BOL: X if (linep == REbolp) X continue; X return 0; X X case AT_EOL: X if (*linep == 0) X continue; X return 0; X X case ANYC: X if (*linep++ != 0) X continue; X return 0; X X case AT_BOW: X if (ismword(*linep) && (linep == REbolp || !ismword(linep[-1]))) X continue; X return 0; X X case AT_EOW: X if ((*linep == 0 || !ismword(*linep)) && X (linep != REbolp && ismword(linep[-1]))) X continue; X return 0; X X case ONE_OF: X case NONE_OF: X if (member(comp_p, *linep++, comp_p[-1] == ONE_OF)) { X comp_p += 16; X continue; X } X return 0; X X case OPENP: X pstrtlst[*comp_p++] = linep; X continue; X X case CLOSEP: X pendlst[*comp_p++] = linep; X continue; X X case BACKREF: X if (pstrtlst[n = *comp_p++] == 0) { X s_mess("\\%d was not specified.", n + 1); X return 0; X } X if (backref(n, linep)) { X linep += pendlst[n] - pstrtlst[n]; X continue; X } X return 0; X X case CURLYB: X { X int wcnt, X any; X X wcnt = *comp_p++; X any = 0; X X while (--wcnt >= 0) { X if (any == 0) X any = REmatch(linep, comp_p + 1); X comp_p += *comp_p; X } X if (any == 0) X return 0; X linep = loc2; X continue; X } X X case ANYC | STAR: X first_p = linep; X while (*linep++) X ; X goto star; X X case NORMC | STAR: X first_p = linep; X while (*comp_p == *linep++) X ; X comp_p += 1; X goto star; X X case CINDC | STAR: X first_p = linep; X while (cind_cmp(*comp_p, *linep++)) X ; X comp_p += 1; X goto star; X X case ONE_OF | STAR: X case NONE_OF | STAR: X first_p = linep; X while (member(comp_p, *linep++, comp_p[-1] == (ONE_OF | STAR))) X ; X comp_p += 16; X goto star; X X case BACKREF | STAR: X first_p = linep; X n = *comp_p++; X while (backref(n, linep)) X linep += pendlst[n] - pstrtlst[n]; X while (linep >= first_p) { X if (REmatch(linep, comp_p)) X return 1; X linep -= pendlst[n] - pstrtlst[n]; X } X continue; X star: do { X linep -= 1; X if (linep < locs) X break; X if (REmatch(linep, comp_p)) X return 1; X } while (linep > first_p); X return 0; X X default: X complain("RE error match (%d).", comp_p[-1]); X } X /* NOTREACHED. */ X} X private void REreset() X{ X register int i; X X for (i = 0; i < NPAR; i++) X pstrtlst[i] = pendlst[i] = 0; X} X X/* Index LINE at OFFSET, the compiled EXPR, with alternates ALTS. If X lbuf_okay is nonzero it's okay to use linebuf if LINE is the current X line. This should save lots of time in things like paren matching in X LISP mode. Saves all that copying from linebuf to REbuf. substitute() X is the guy who calls re_lindex with lbuf_okay as 0, since the substitution X gets placed in linebuf ... doesn't work too well when the source and X destination strings are the same. I hate all these arguments! X X This code is cumbersome, repetetive for reasons of efficiency. Fast X search is a must as far as I am concerned. */ X int re_lindex(line, offset, expr, alts, lbuf_okay) Line *line; char *expr, X **alts; X{ X int isquick; X register int firstc, X c; X register char *resp; X X REreset(); X if (lbuf_okay) { X REbolp = lbptr(line); X if (offset == -1) X offset = strlen(REbolp); /* arg! */ X } else { X REbolp = ltobuf(line, REbuf); X if (offset == -1) { /* Reverse search, find end of line. */ X extern int Jr_Len; X X offset = Jr_Len; /* Just Read Len. */ X } X } X resp = REbolp; X isquick = ((expr[0] == NORMC || expr[0] == CINDC) && X (alternates[1] == 0)); X if (isquick) { X firstc = expr[2]; X if (expr[0] == CINDC) X firstc = CaseEquiv[firstc]; X } X locs = REbolp + offset; X X if (REdirection == FORWARD) { X do { X char **altp = alts; X X if (isquick) { X if (expr[0] == NORMC) X while ((c = *locs++) != 0 && c != firstc) X ; X else X while (((c = *locs++) != 0) && X (CaseEquiv[c] != firstc)) X ; X if (*--locs == 0) X break; X } X REalt_num = 1; X while (*altp) { X if (REmatch(locs, *altp++)) { X loc1 = locs; X REbom = loc1 - REbolp; X return 1; X } X REalt_num += 1; X } X } while (*locs++); X } else { X do { X char **altp = alts; X X if (isquick) { X if (expr[0] == NORMC) { X while (locs >= REbolp && *locs-- != firstc) X ; X if (*++locs != firstc) X break; X } else { X while (locs >= REbolp && CaseEquiv[*locs--] != firstc) X ; X if (CaseEquiv[*++locs] != firstc) X break; X } X } X REalt_num = 1; X while (*altp) { X if (REmatch(locs, *altp++)) { X loc1 = locs; X REbom = loc1 - REbolp; X return 1; X } X REalt_num += 1; X } X } while (--locs >= resp); X } X X return 0; X} X int okay_wrap = 0; /* Do a wrap search ... not when we're X parsing errors ... */ X Bufpos * dosearch(pattern, dir, re) char *pattern; X{ X Bufpos *pos; X X if (bobp() && eobp()) /* Can't match! There's no buffer. */ X return 0; X X REcompile(pattern, re, compbuf, alternates); X X pos = docompiled(dir, compbuf, alternates); X return pos; X} X Bufpos * docompiled(dir, expr, alts) char *expr, X **alts; X{ X static Bufpos ret; X register Line *lp; X register int offset; X int we_wrapped = NO; X X lsave(); X /* Search now lsave()'s so it doesn't make any assumptions on X whether the the contents of curline/curchar are in linebuf. X Nowhere does search write all over linebuf. However, we have to X be careful about what calls we make here, because many of them X assume (and rightly so) that curline is in linebuf. */ X X REdirection = dir; X lp = curline; X offset = curchar; X if (dir == BACKWARD) { X if (bobp()) { X if (okay_wrap && WrapScan) X goto doit; X return 0; X } X /* here we simulate BackChar() */ X if (bolp()) { X lp = lp->l_prev; X offset = strlen(lbptr(lp)); X } else X offset -= 1; X } else if ((dir == FORWARD) && X (lbptr(lp)[offset] == '\0') && X !lastp(lp)) { X lp = lp->l_next; X offset = 0; X } X X do { X if (re_lindex(lp, offset, expr, alts, YES)) X break; doit: lp = (dir == FORWARD) ? lp->l_next : lp->l_prev; X if (lp == 0) { X if (okay_wrap && WrapScan) { X lp = (dir == FORWARD) ? X curbuf->b_first : curbuf->b_last; X we_wrapped = YES; X } else X break; X } X if (dir == FORWARD) X offset = 0; X else X offset = -1; /* signals re_lindex ... */ X } while (lp != curline); X X if (lp == curline && we_wrapped) X lp = 0; X if (lp == 0) X return 0; X ret.p_line = lp; X ret.p_char = (dir == FORWARD) ? REeom : REbom; X return &ret; X} X private char * insert(off, endp, which) char *off, X *endp; X{ X register char *pp; X register int n; X X n = pendlst[which] - pstrtlst[which]; X pp = pstrtlst[which]; X while (--n >= 0) { X *off++ = *pp++; X if (off >= endp) X len_error(ERROR); X } X return off; X} X X/* Perform the substitution. If DELP is nonzero the matched string is X deleted, i.e., the substitution string is not inserted. */ X void re_dosub(tobuf, delp) char *tobuf; X{ X register char *tp, X *rp, X *repp; X int c; X char *endp; X X tp = tobuf; X endp = tp + LBSIZE; X rp = REbuf; X repp = rep_str; X X while (rp < loc1) X *tp++ = *rp++; X X if (!delp) while (c = *repp++) { X if (c == '\\') { X c = *repp++; X if (c == '\0') { X *tp++ = '\\'; X goto endchk; X } else if (c >= '1' && c <= nparens + '1') { X tp = insert(tp, endp, c - '0'); X continue; X } X } else if (c == '&') { X tp = insert(tp, endp, 0); X continue; X } X *tp++ = c; endchk: if (tp >= endp) X len_error(ERROR); X } X rp = loc2; X loc2 = REbuf + max(1, tp - tobuf); X REeom = loc2 - REbuf; X /* At least one character past the match, to prevent an infinite X number of replacements in the same position, e.g., X replace "^" with "". */ X while (*tp++ = *rp++) X if (tp >= endp) X len_error(ERROR); X} X void putmatch(which, buf, size) char *buf; X{ X *(insert(buf, buf + size, which)) = 0; X} X void setsearch(str) char *str; X{ X strcpy(searchstr, str); X} X char * getsearch() X{ X return searchstr; X} X void RErecur() X{ X char sbuf[sizeof searchstr], X cbuf[sizeof compbuf], X repbuf[sizeof rep_str], X *altbuf[NALTS]; X int npars; X Mark *m = MakeMark(curline, REbom, M_FLOATER); X X message("Type C-X C-C to continue with query replace."); X X npars = nparens; X byte_copy(compbuf, cbuf, sizeof compbuf); X byte_copy(searchstr, sbuf, sizeof searchstr); X byte_copy(rep_str, repbuf, sizeof rep_str); X byte_copy((char *) alternates, (char *) altbuf, sizeof alternates); X Recur(); X nparens = npars; X byte_copy(cbuf, compbuf, sizeof compbuf); X byte_copy(sbuf, searchstr, sizeof searchstr); X byte_copy(repbuf, rep_str, sizeof rep_str); X byte_copy((char *) altbuf, (char *) alternates, sizeof alternates); X if (!is_an_arg()) X ToMark(m); X DelMark(m); X} X void ForSearch() X{ X search(FORWARD, UseRE, YES); X} X void RevSearch() X{ X search(BACKWARD, UseRE, YES); X} X void FSrchND() X{ X search(FORWARD, UseRE, NO); X} X void RSrchND() X{ X search(BACKWARD, UseRE, NO); X} X private void search(dir, re, setdefault) X{ X Bufpos *newdot; X char *s; X X s = ask(searchstr, ProcFmt); X if (setdefault) X setsearch(s); X okay_wrap = YES; X newdot = dosearch(s, dir, re); X okay_wrap = NO; X if (newdot == 0) { X if (WrapScan) X complain("No \"%s\" in buffer.", s); X else X complain("No \"%s\" found to %s.", s, X (dir == FORWARD) ? "bottom" : "top"); X } X PushPntp(newdot->p_line); X SetDot(newdot); X} X X/* Do we match PATTERN at OFFSET in BUF? */ X int LookingAt(pattern, buf, offset) char *pattern, X *buf; X{ X register char **alt = alternates; X X REcompile(pattern, 1, compbuf, alternates); X REreset(); X locs = buf + offset; X REbolp = buf; X X while (*alt) X if (REmatch(locs, *alt++)) X return 1; X return 0; X} X int look_at(expr) char *expr; X{ X REcompile(expr, 0, compbuf, alternates); X REreset(); X locs = linebuf + curchar; X REbolp = linebuf; X if (REmatch(locs, alternates[0])) X return 1; X return 0; X} X END_OF_FILE if test 18437 -ne `wc -c <'./re.c'`; then echo shar: \"'./re.c'\" unpacked with wrong size! fi # end of './re.c' fi echo shar: End of archive 10 \(of 21\). cp /dev/null ark10isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 21 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.