home *** CD-ROM | disk | FTP | other *** search
- /************************************************************************
- * This program is Copyright (C) 1986 by Jonathan Payne. JOVE is *
- * provided to you without charge, and with no warranty. You may give *
- * away copies of JOVE, including sources, provided that this notice is *
- * included in all the files. *
- ************************************************************************/
-
- #include "jove.h"
- #include "io.h"
- #include "termcap.h"
- #include "ctype.h"
- #ifdef JOB_CONTROL
- # include <signal.h>
- #endif
-
- #include <varargs.h>
-
- int InJoverc = 0;
-
- extern int getch(),
- getchar();
-
- /* Auto execute code */
-
- #define NEXECS 20
-
- private struct {
- char *a_pattern;
- data_obj *a_cmd;
- } AutoExecs[NEXECS] = {0};
-
- private int ExecIndex = 0;
-
- /* Command auto-execute. */
-
- CAutoExec()
- {
- DefAutoExec(findcom);
- }
-
- /* Macro auto-execute. */
-
- MAutoExec()
- {
- DefAutoExec(findmac);
- }
-
- /* VARARGS0 */
-
- DefAutoExec(proc)
- data_obj *(*proc)();
- {
- data_obj *d;
- char *pattern;
- int i;
-
- if (ExecIndex >= NEXECS)
- complain("Too many auto-executes, max %d.", NEXECS);
- if ((d = (*proc)(ProcFmt)) == 0)
- return;
- pattern = ask((char *) 0, ": %f %s ", d->Name);
- for (i = 0; i < ExecIndex; i++)
- if ((AutoExecs[i].a_cmd == d) &&
- (strcmp(pattern, AutoExecs[i].a_pattern) == 0))
- return; /* Eliminate duplicates. */
- AutoExecs[ExecIndex].a_pattern = copystr(pattern);
- AutoExecs[ExecIndex].a_cmd = d;
- ExecIndex++;
- }
-
- /* DoAutoExec: NEW and OLD are file names, and if NEW and OLD aren't the
- same kind of file (i.e., match the same pattern) or OLD is 0 and it
- matches, we execute the command associated with that kind of file. */
-
- DoAutoExec(new, old)
- register char *new,
- *old;
- {
- register int i;
-
- exp_p = YES;
- exp = 1; /* So minor modes don't toggle. We always want
- them on. */
- if (new == 0)
- return;
- for (i = 0; i < ExecIndex; i++)
- if ((LookingAt(AutoExecs[i].a_pattern, new, 0)) &&
- (old == 0 || !LookingAt(AutoExecs[i].a_pattern, old, 0)))
- ExecCmd(AutoExecs[i].a_cmd);
- }
-
- BindAKey()
- {
- BindSomething(findcom);
- }
-
- BindMac()
- {
- BindSomething(findmac);
- }
-
- extern int EscPrefix(),
- CtlxPrefix(),
- MiscPrefix();
-
- data_obj **
- IsPrefix(cp)
- data_obj *cp;
- {
- int (*proc)();
-
- if (cp == 0 || (cp->Type & TYPEMASK) != FUNCTION)
- return 0;
- proc = ((struct cmd *) cp)->c_proc;
- if (proc == EscPrefix)
- return pref1map;
- if (proc == CtlxPrefix)
- return pref2map;
- if (proc == MiscPrefix)
- return miscmap;
- return 0;
- }
-
- unbind_aux(c)
- {
- if (c == CR || c == LF)
- return FALSE; /* tells do_ask to return */
- Insert(c);
- return !FALSE;
- }
-
- UnbindC()
- {
- char *keys;
- data_obj **map = mainmap;
-
- keys = do_ask("\r\n\01\02\03\04\05\06\010\011\013\014\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037", unbind_aux, (char *) 0, ProcFmt);
- if (keys == 0)
- return;
- for (;;) {
- if (keys[1] == '\0')
- break;
- if ((map = IsPrefix(map[*keys])) == 0)
- break;
- keys++;
- }
- if (keys[1] != 0)
- complain("That's not a legitimate key sequence.");
- map[keys[0]] = 0;
- }
-
- addgetc()
- {
- int c;
-
- if (!InJoverc)
- Asking = strlen(mesgbuf);
- c = getch();
- if (InJoverc) {
- if (c == '\n')
- return EOF; /* this isn't part of the sequence */
- else if (c == '\\') {
- if ((c = getch()) == LF)
- complain("[Premature end of line]");
- } else if (c == '^') {
- if ((c = getch()) == '?')
- c = RUBOUT;
- else if (isalpha(c) || index("[\\]^_", c))
- c = c - '@';
- else
- complain("[Unknown control character]");
- }
- }
-
- Asking = 0;
- add_mess("%p ", c);
-
- return c;
- }
-
- BindWMap(map, lastkey, cmd)
- data_obj **map,
- *cmd;
- {
- data_obj **nextmap;
- int c;
-
- c = addgetc();
- if (c == EOF) {
- if (lastkey == EOF)
- complain("[Empty key sequence]");
- complain("[Premature end of key sequence]");
- } else {
- if (nextmap = IsPrefix(map[c]))
- BindWMap(nextmap, c, cmd);
- else
- map[c] = cmd;
- }
- }
-
- /* VARARGS0 */
-
- BindSomething(proc)
- data_obj *(*proc)();
- {
- data_obj *d;
-
- if ((d = (*proc)(ProcFmt)) == 0)
- return;
- s_mess(": %f %s ", d->Name);
- BindWMap(mainmap, EOF, d);
- }
-
- /* Describe key */
-
- DescWMap(map, key)
- data_obj **map;
- {
- data_obj *cp = map[key],
- **prefp;
-
- if (cp == 0)
- add_mess("is unbound.");
- else if (prefp = IsPrefix(cp))
- DescWMap(prefp, addgetc());
- else
- add_mess("is bound to %s.", cp->Name);
- }
-
- KeyDesc()
- {
- s_mess(ProcFmt);
- DescWMap(mainmap, addgetc());
- }
-
- DescCom()
- {
- data_obj *dp;
- char pattern[100],
- doc_type[40],
- *file = CmdDb;
- File *fp;
-
- if (!strcmp(LastCmd->Name, "describe-variable"))
- dp = (data_obj *) findvar(ProcFmt);
- else
- dp = (data_obj *) findcom(ProcFmt);
-
- if (dp == 0)
- return;
- fp = open_file(file, iobuff, F_READ, COMPLAIN, QUIET);
- Placur(ILI, 0);
- flusho();
- sprintf(pattern, "^:entry \"%s\" \"\\([^\"]*\\)\"", dp->Name);
- TOstart("Help", TRUE);
- for (;;) {
- if (f_gets(fp, genbuf, LBSIZE) == EOF) {
- Typeout("There is no documentation for \"%s\".", dp->Name);
- goto outahere;
- }
- if ((strncmp(genbuf, ":entry", 6) == 0) && LookingAt(pattern, genbuf, 0))
- break;
- }
- /* found it ... let's print it */
- putmatch(1, doc_type, sizeof doc_type);
- if (strcmp("Variable", doc_type) == 0)
- Typeout(dp->Name);
- else if (strcmp("Command", doc_type) == 0) {
- char binding[128];
-
- find_binds(dp, binding);
- if (blnkp(binding))
- Typeout("To invoke %s, type \"ESC X %s<cr>\".",
- dp->Name,
- dp->Name);
- else
- Typeout("Type \"%s\" to invoke %s.", binding, dp->Name);
- }
- Typeout("");
- while (f_gets(fp, genbuf, LBSIZE) != EOF)
- if (strncmp(genbuf, ":entry", 6) == 0)
- goto outahere;
- else
- Typeout("%s", genbuf);
- outahere:
- f_close(fp);
- TOstop();
- }
-
- DescBindings()
- {
- extern int Typeout();
-
- TOstart("Key Bindings", TRUE);
- DescMap(mainmap, NullStr);
- TOstop();
- }
-
- DescMap(map, pref)
- data_obj **map;
- char *pref;
- {
- int c1,
- c2 = 0,
- numbetween;
- char keydescbuf[40];
- data_obj **prefp;
-
- for (c1 = 0; c1 < 0200 && c2 < 0200; c1 = c2 + 1) {
- c2 = c1;
- if (map[c1] == 0)
- continue;
- while (++c2 < 0200 && map[c1] == map[c2])
- ;
- c2--;
- numbetween = c2 - c1;
- if (numbetween == 1)
- sprintf(keydescbuf, "%s {%p,%p}", pref, c1, c2);
- else if (numbetween == 0)
- sprintf(keydescbuf, "%s %p", pref, c1);
- else
- sprintf(keydescbuf, "%s [%p-%p]", pref, c1, c2);
- if (prefp = IsPrefix(map[c1]))
- DescMap(prefp, keydescbuf);
- else
- Typeout("%-14s%s", keydescbuf, map[c1]->Name);
- }
- }
-
- private
- find_binds(dp, buf)
- data_obj *dp;
- char *buf;
- {
- char *endp;
-
- buf[0] = '\0';
- fb_aux(dp, mainmap, (char *) 0, buf);
- endp = buf + strlen(buf) - 2;
- if ((endp > buf) && (strcmp(endp, ", ") == 0))
- *endp = '\0';
- }
-
- private
- fb_aux(cp, map, prefix, buf)
- register data_obj *cp,
- **map;
- char *buf,
- *prefix;
- {
- int c1,
- c2;
- char *bufp = buf + strlen(buf),
- prefbuf[20];
- data_obj **prefp;
-
- for (c1 = c2 = 0; c1 < 0200 && c2 < 0200; c1 = c2 + 1) {
- c2 = c1;
- if (map[c1] == cp) {
- while (++c2 < 0200 && map[c1] == map[c2])
- ;
- c2--;
- if (prefix)
- sprintf(bufp, "%s ", prefix);
- bufp += strlen(bufp);
- switch (c2 - c1) {
- case 0:
- sprintf(bufp, "%p, ", c1);
- break;
-
- case 1:
- sprintf(bufp, "{%p,%p}, ", c1, c2);
- break;
-
- default:
- sprintf(bufp, "[%p-%p], ", c1, c2);
- break;
- }
- }
- if (prefp = IsPrefix(map[c1])) {
- sprintf(prefbuf, "%p", c1);
- fb_aux(cp, prefp, prefbuf, bufp);
- }
- bufp += strlen(bufp);
- }
- }
-
- Apropos()
- {
- register struct cmd *cp;
- register struct macro *m;
- register struct variable *v;
- char *ans;
- int anyfs = 0,
- anyvs = 0,
- anyms = 0;
- char buf[256];
-
- ans = ask((char *) 0, ": %f (keyword) ");
- TOstart("Help", TRUE);
- for (cp = commands; cp->Name != 0; cp++)
- if (sindex(ans, cp->Name)) {
- if (anyfs == 0) {
- Typeout("Commands");
- Typeout("--------");
- }
- find_binds((data_obj *) cp, buf);
- if (buf[0])
- Typeout(": %-35s(%s)", cp->Name, buf);
- else
- Typeout(": %s", cp->Name);
- anyfs++;
- }
- if (anyfs)
- Typeout(NullStr);
- for (v = variables; v->Name != 0; v++)
- if (sindex(ans, v->Name)) {
- if (anyvs == 0) {
- Typeout("Variables");
- Typeout("---------");
- }
- anyvs++;
- vpr_aux(v, buf);
- Typeout(": set %-26s%s", v->Name, buf);
- }
- if (anyvs)
- Typeout(NullStr);
- for (m = macros; m != 0; m = m->m_nextm)
- if (sindex(ans, m->Name)) {
- if (anyms == 0) {
- Typeout("Macros");
- Typeout("------");
- }
- anyms++;
- find_binds((data_obj *) m, buf);
- if (buf[0])
- Typeout(": %-35s(%s)", m->Name, buf);
- else
- Typeout(": %-35s%s", "execute-macro", m->Name);
- }
- TOstop();
- }
-
- Extend()
- {
- data_obj *d;
-
- if (d = findcom(": "))
- ExecCmd(d);
- }
-
- /* Read a positive integer from CP. It must be in base BASE, and
- complains if it isn't. If allints is nonzero, all the characters
- in the string must be integers or we return -1; otherwise we stop
- reading at the first nondigit. */
-
- chr_to_int(cp, base, allints)
- register char *cp;
- {
- register int c;
- int value = 0;
-
- while (c = *cp++) {
- if (!isdigit(c)) {
- if (allints)
- return -1;
- break;
- }
- c = c - '0';
- if (c >= base)
- complain("You must specify in base %d.", base);
- value = value * base + c;
- }
- return value;
- }
-
- ask_int(prompt, base)
- char *prompt;
- int base;
- {
- char *val = ask((char *) 0, prompt);
- int value = chr_to_int(val, base, 1);
-
- if (value < 0)
- complain("That's not a number!");
- return value;
- }
-
- private
- vpr_aux(vp, buf)
- register struct variable *vp;
- char *buf;
- {
- switch (vp->v_flags & V_TYPEMASK) {
- case V_BASE10:
- sprintf(buf, "%d", *(vp->v_value));
- break;
-
- case V_BASE8:
- sprintf(buf, "%o", *(vp->v_value));
- break;
-
- case V_BOOL:
- sprintf(buf, (*(vp->v_value)) ? "on" : "off");
- break;
-
- case V_STRING:
- case V_FILENAME:
- sprintf(buf, "%s", (char *) vp->v_value);
- break;
-
- case V_CHAR:
- sprintf(buf, "%p", *(vp->v_value));
- break;
- }
- }
-
- PrVar()
- {
- struct variable *vp;
- char prbuf[256];
-
- if ((vp = (struct variable *) findvar(ProcFmt)) == 0)
- return;
- vpr_aux(vp, prbuf);
- s_mess(": %f %s => %s", vp->Name, prbuf);
- }
-
- SetVar()
- {
- struct variable *vp;
- char *prompt;
-
- if ((vp = (struct variable *) findvar(ProcFmt)) == 0)
- return;
- prompt = sprint(": %f %s ", vp->Name);
-
- switch (vp->v_flags & V_TYPEMASK) {
- case V_BASE10:
- case V_BASE8:
- {
- int value;
-
- value = ask_int(prompt, ((vp->v_flags & V_TYPEMASK) == V_BASE10)
- ? 10 : 8);
- *(vp->v_value) = value;
- break;
- }
-
- case V_BOOL:
- {
- char *def = *(vp->v_value) ? "off" : "on",
- *on_off;
- int value;
-
- on_off = ask(def, prompt);
- if (casecmp(on_off, "on") == 0)
- value = ON;
- else if (casecmp(on_off, "off") == 0)
- value = OFF;
- else
- complain("Boolean variables must be ON or OFF.");
- *(vp->v_value) = value;
- s_mess("%s%s", prompt, value ? "on" : "off");
- break;
- }
-
- case V_FILENAME:
- {
- char fbuf[FILESIZE];
-
- sprintf(&prompt[strlen(prompt)], "(default %s) ", vp->v_value);
- (void) ask_file(prompt, (char *) vp->v_value, fbuf);
- strcpy((char *) vp->v_value, fbuf);
- break;
- }
-
- case V_STRING:
- {
- char *str;
-
- /* Do_ask() so you can set string to "" if you so desire. */
- str = do_ask("\r\n", (int (*)()) 0, (char *) vp->v_value, prompt);
- if (str == 0)
- str = NullStr;
- strcpy((char *) vp->v_value, str);
- /* ... and hope there is enough room. */
- break;
- }
- case V_CHAR:
- f_mess(prompt);
- *(vp->v_value) = addgetc();
- break;
-
- }
- if (vp->v_flags & V_MODELINE)
- UpdModLine++;
- if (vp->v_flags & V_CLRSCREEN)
- ClAndRedraw();
- if (vp->v_flags & V_TTY_RESET)
- tty_reset();
- }
-
- /* Command completion - possible is an array of strings, prompt is
- the prompt to use, and flags are ... well read jove.h.
-
- If flags are RET_STATE, and the user hits <return> what they typed
- so far is in the Minibuf string. */
-
- private char **Possible;
- private int comp_value,
- comp_flags;
-
- aux_complete(c)
- {
- int command,
- length,
- i;
-
- if (comp_flags & CASEIND) {
- char *lp;
-
- for (lp = linebuf; *lp != '\0'; lp++)
- if (isupper(*lp))
- *lp = tolower(*lp);
- }
- switch (c) {
- case EOF:
- comp_value = -1;
- return 0;
-
- case '\r':
- case '\n':
- command = match(Possible, linebuf);
- if (command >= 0) {
- comp_value = command;
- return 0; /* tells ask to stop */
- }
- if (eolp() && bolp()) {
- comp_value = NULLSTRING;
- return 0;
- }
- if (comp_flags & RET_STATE) switch (command) {
- case UNIQUE:
- case ORIGINAL:
- case NULLSTRING:
- comp_value = command;
- return 0;
-
- default:
- break;
- }
- if (InJoverc)
- complain("[\"%s\" unknown]", linebuf);
- rbell();
- break;
-
- case '\t':
- case ' ':
- {
- int minmatch = 1000,
- maxmatch = 0,
- numfound = 0,
- lastmatch = -1,
- length = strlen(linebuf);
-
- for (i = 0; Possible[i] != 0; i++) {
- int this_len;
-
- this_len = numcomp(Possible[i], linebuf);
- maxmatch = max(maxmatch, this_len);
- if (this_len >= length) {
- if (numfound)
- minmatch = min(minmatch, numcomp(Possible[lastmatch], Possible[i]));
- else
- minmatch = strlen(Possible[i]);
- numfound++;
- lastmatch = i;
- if (strcmp(linebuf, Possible[i]) == 0)
- break;
- }
- }
-
- if (numfound == 0) {
- rbell();
- if (InJoverc)
- complain("[\"%s\" unknown]", linebuf);
- /* If we're not in the .joverc then
- let's do something helpful for the
- user. */
- if (maxmatch < length) {
- char *cp;
-
- cp = linebuf + maxmatch;
- *cp = 0;
- Eol();
- }
- break;
- }
- if (c != '\t' && numfound == 1) {
- comp_value = lastmatch;
- return 0;
- }
- null_ncpy(linebuf, Possible[lastmatch], minmatch);
- Eol();
- if (minmatch == length) /* No difference */
- rbell();
- break;
- }
-
- case '?':
- if (InJoverc)
- complain((char *) 0);
- /* kludge: in case we're using UseBuffers, in which case
- linebuf gets written all over */
- strcpy(Minibuf, linebuf);
- length = strlen(Minibuf);
- TOstart("Completion", TRUE); /* for now ... */
- for (i = 0; Possible[i]; i++)
- if (numcomp(Possible[i], Minibuf) >= length) {
- Typeout(Possible[i]);
- if (TOabort != 0)
- break;
- }
-
- TOstop();
- break;
- }
- return !FALSE;
- }
-
- complete(possible, prompt, flags)
- register char *possible[];
- char *prompt;
- {
- Possible = possible;
- comp_flags = flags;
- (void) do_ask("\r\n \t?", aux_complete, NullStr, prompt);
- return comp_value;
- }
-
- match(choices, what)
- register char **choices,
- *what;
- {
- register int len;
- int i,
- found = 0,
- save,
- exactmatch = -1;
-
- len = strlen(what);
- if (len == 0)
- return NULLSTRING;
- for (i = 0; choices[i]; i++) {
- if (strncmp(what, choices[i], len) == 0) {
- if (strcmp(what, choices[i]) == 0)
- exactmatch = i;
- save = i;
- found++; /* Found one. */
- }
- }
-
- if (found == 0)
- save = ORIGINAL;
- else if (found > 1) {
- if (exactmatch != -1)
- save = exactmatch;
- else
- save = AMBIGUOUS;
- }
-
- return save;
- }
-
- Source()
- {
- char *com,
- buf[FILESIZE];
-
- sprintf(buf, "%s/.joverc", getenv("HOME"));
- com = ask_file((char *) 0, buf, buf);
- if (joverc(buf) == NIL)
- complain(IOerr("read", com));
- }
-
- BufPos()
- {
- register Line *lp = curbuf->b_first;
- register int i,
- dotline;
- long dotchar,
- nchars;
-
- for (i = nchars = 0; lp != 0; i++, lp = lp->l_next) {
- if (lp == curline) {
- dotchar = nchars + curchar;
- dotline = i + 1;
- }
- nchars += length(lp) + (lp->l_next != 0); /* include the NL */
- }
-
- s_mess("[\"%s\" line %d of %d, char %D of %D (%d%%)]",
- filename(curbuf),
- dotline,
- i,
- dotchar,
- nchars,
- (int) (((long) dotchar * 100) / nchars));
- }
-
- #define IF_UNBOUND -1
- #define IF_TRUE 1
- #define IF_FALSE !IF_TRUE
-
- do_if(cmd)
- char *cmd;
- {
- int pid,
- status;
-
- switch (pid = fork()) {
- case -1:
- complain("[Fork failed: if]");
-
- case 0:
- {
- char *args[12],
- *cp = cmd,
- **ap = args;
-
- *ap++ = cmd;
- for (;;) {
- if ((cp = index(cp, ' ')) == 0)
- break;
- *cp++ = '\0';
- *ap++ = cp;
- }
- *ap = 0;
-
- close(0); /* we want reads to fail */
- /* close(1); but not writes or ioctl's
- close(2); */
-
- (void) execvp(args[0], args);
- _exit(-10); /* signals exec error (see below) */
- }
- }
- #ifdef IPROCS
- sighold(SIGCHLD);
- #endif
- dowait(pid, &status);
- #ifdef IPROCS
- sigrelse(SIGCHLD);
- #endif
- if (status == -10)
- complain("[Exec failed]");
- if (status < 0)
- complain("[Exit %d]", status);
- return (status == 0); /* 0 means successful */
- }
-
- joverc(file)
- char *file;
- {
- char buf[LBSIZE],
- lbuf[128];
- int lnum = 0,
- eof = FALSE;
- jmp_buf savejmp;
- int IfStatus = IF_UNBOUND;
- File *fp;
-
- fp = open_file(file, buf, F_READ, !COMPLAIN, QUIET);
- if (fp == NIL)
- return NIL;
-
- /* Catch any errors, here, and do the right thing with them,
- and then restore the error handle to whoever did a setjmp
- last. */
-
- push_env(savejmp);
- if (setjmp(mainjmp)) {
- Buffer *savebuf = curbuf;
-
- SetBuf(do_select((Window *) 0, "RC errors"));
- ins_str(sprint("%s:%d:%s\t%s\n", pr_name(file), lnum, lbuf, mesgbuf), NO);
- unmodify();
- SetBuf(savebuf);
- Asking = 0;
- }
- InJoverc = 1;
- if (!eof) do {
- eof = (f_gets(fp, lbuf, sizeof lbuf) == EOF);
- lnum++;
- if (casencmp(lbuf, "if", 2) == 0) {
- char cmd[128];
-
- if (IfStatus != IF_UNBOUND)
- complain("[Cannot have nested if's]");
- if (LookingAt("if[ \t]*\\(.*\\)$", lbuf, 0) == 0)
- complain("[If syntax error]");
- putmatch(1, cmd, sizeof cmd);
- IfStatus = do_if(cmd) ? IF_TRUE : IF_FALSE;
- continue;
- } else if (casencmp(lbuf, "else", 4) == 0) {
- if (IfStatus == IF_UNBOUND)
- complain("[Unexpected `else']");
- IfStatus = !IfStatus;
- continue;
- } else if (casencmp(lbuf, "endif", 5) == 0) {
- if (IfStatus == IF_UNBOUND)
- complain("[Unexpected `endif']");
- IfStatus = IF_UNBOUND;
- continue;
- }
- if (IfStatus == IF_FALSE)
- continue;
- (void) strcat(lbuf, "\n");
- Inputp = lbuf;
- while (*Inputp == ' ' || *Inputp == '\t')
- Inputp++; /* skip white space */
- Extend();
- } while (!eof);
-
- f_close(fp);
- pop_env(savejmp);
- Inputp = 0;
- Asking = 0;
- InJoverc = 0;
- if (IfStatus != IF_UNBOUND)
- complain("[Missing endif]");
- return !NIL;
- }
-