/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */ static char rcsid[] = "$Header: demo.c,v 2.6 85/08/22 16:01:21 timo Exp $"; /* * B editor -- Editor command processor. */ #include #include "b.h" #include "feat.h" #include "erro.h" #include "bobj.h" #include "node.h" #include "gram.h" #include "keys.h" #include "supr.h" #ifdef BTOP #include #ifndef CMDPROMPT #define CMDPROMPT ">>> " /* Prompt user for immediate command */ #endif CMDPROMPT #endif BTOP value editqueue(); /* Command line flags */ extern bool dflag; extern bool slowterminal; #ifdef SAVEBUF extern char copysavefile[]; #endif SAVEBUF Visible bool lefttorite; /* Saves some time in nosuggtoqueue() for read from file */ #define MAXHIST 101 /* One more than the number of UNDO's allowed. */ #define Mod(k) (((k)+MAXHIST) % MAXHIST) #define Succ(k) (((k)+1) % MAXHIST) #define Pred(k) (((k)+MAXHIST-1) % MAXHIST) Hidden environ *tobesaved; Hidden string savewhere; #ifdef BTOP extern jmp_buf jumpback; extern bool interrupted; extern bool canjump; /* * Main loop, called from main program if -t option present. */ Visible Procedure mainloop() { environ env; environ *ep = &env; FILE *pdown; FILE *pup; int cmdchar; savewhere = (string)NULL; tobesaved = (environ*)NULL; start_b(&pdown, &pup); clrenv(ep); #ifdef SAVEBUF ep->copybuffer = editqueue(copysavefile); if (ep->copybuffer) ep->copyflag = Yes; #endif SAVEBUF for (;;) { cmdchar = sleur(); if (cmdchar == EOF) break; getinput(ep, cmdchar, pdown, pup); } #ifdef SAVEBUF if (ep->copyflag) savequeue(ep->copybuffer, copysavefile); else savequeue(Vnil, copysavefile); #endif SAVEBUF Erelease(*ep); } /* * Provide input for the interpreter. */ Hidden Procedure getinput(ep, cmdchar, pdown, pup) environ *ep; int cmdchar; FILE *pdown; FILE *pup; { int n; char buffer[100]; char filename[100]; int lineno; switch (cmdchar) { case '>': /* Immediate command */ case 'E': /* Expression */ case 'R': /* Raw input */ case 'Y': /* Yes/No */ if (cmdchar == '>') setroot("Imm_cmd"); else if (cmdchar == 'E') setroot("Expression"); else setroot("Raw_input"); delfocus(&ep->focus); initshow(); if (cmdchar == '>') cmdprompt(CMDPROMPT); editdocument(ep); endshow(); top(&ep->focus); ep->mode = WHOLE; if (!interrupted) send(ep->focus, pdown); delete(ep); break; case ':': case '=': fgets(buffer, sizeof buffer, pup); if (index(buffer, '+')) n = sscanf(buffer, " +%d %s", &lineno, filename) - 1; else { n = sscanf(buffer, " %s", filename); lineno = 0; } if (n == 1) { initshow(); dofile(ep, filename, lineno); endshow(); top(&ep->focus); ep->mode = WHOLE; delete(ep); if (!ep->copyflag) { release(ep->copybuffer); ep->copybuffer = Vnil; } } putc('\n', pdown); interrupted = No; /* Interrupts handled locally in editdocument! */ break; default: printf("[Unrecognized command character '%c' (0%o)]\n", cmdchar&0177, cmdchar); } } #endif BTOP #ifdef FILEARGS /* * Edit a single unit or target, called from main program if file name * arguments are present. */ Visible Procedure demo(filename, linenumber) string filename; int linenumber; { environ env; environ *ep = &env; bool ok; clrenv(ep); #ifdef SAVEBUF ep->copybuffer = editqueue(copysavefile); if (ep->copybuffer) ep->copyflag = Yes; #endif SAVEBUF initshow(); ok = dofile(ep, filename, linenumber); endshow(); if (!ok) return No; #ifdef SAVEBUF if (ep->copyflag) savequeue(ep->copybuffer, copysavefile); else savequeue(Vnil, copysavefile); #endif SAVEBUF Erelease(*ep); return Yes; } #endif !FILEARGS /* * Edit a unit or target, using the environment offered as a parameter. */ Hidden bool dofile(ep, filename, linenumber) environ *ep; string filename; int linenumber; { #ifdef HELPFUL static bool didmessage; if (!didmessage) { didmessage = Yes; message("[Press ? or ESC-? for help]"); } #endif HELPFUL #ifdef SAVEPOS if (linenumber <= 0) linenumber = getpos(filename); #endif SAVEPOS setroot(filename[0] == '=' ? "Target_edit" : "Unit_edit"); savewhere = filename; tobesaved = (environ*)NULL; lefttorite = Yes; edit(ep, filename, linenumber); #ifdef USERSUGG readsugg(ep->focus); #endif USERSUGG lefttorite = No; ep->generation = 0; if (!editdocument(ep)) return No; if (ep->generation > 0) { if (!save(ep->focus, filename)) error("Cannot save unit: %s", unixerror(filename)); #ifdef USERSUGG writesugg(ep->focus); #endif USERSUGG } #ifdef SAVEPOS savepos(filename, lineno(ep)+1); #endif SAVEPOS savewhere = (char*)NULL; tobesaved = (environ*)NULL; return Yes; } /* * Call the editor for a given document. */ Hidden bool editdocument(ep) environ *ep; { int k; int first = 0; int last = 0; int current = 0; int onscreen = -1; bool reverse = No; environ newenv; int cmd; bool errors = No; int undoage = 0; bool done = No; environ history[MAXHIST]; Ecopy(*ep, history[0]); for (;;) { /* Command interpretation loop */ if (onscreen != current) virtupdate(onscreen < 0 ? (environ*)NULL : &history[onscreen], &history[current], reverse && onscreen >= 0 ? history[onscreen].highest : history[current].highest); onscreen = current; if (done) break; #ifdef BTOP if (!interrupted && !moreinput()) #else BTOP if (!moreinput()) #endif BTOP actupdate(history[current].copyflag ? history[current].copybuffer : Vnil, #ifdef RECORDING history[current].newmacro != Vnil, #else !RECORDING No, #endif !RECORDING No); #ifdef BTOP if (interrupted || setjmp(jumpback)) break; canjump = Yes; #endif BTOP cmd = inchar(); #ifdef BTOP canjump = No; #endif BTOP errors = No; switch (cmd) { #ifndef NDEBUG case Ctl(@): /* Debug exit with variable dump */ tobesaved = (environ*)NULL; return No; #endif NDEBUG #ifndef SMALLSYS case Ctl(^): /* Debug status message */ dbmess(&history[current]); errors = Yes; /* Causes clear after new keystroke seen */ continue; #endif !SMALLSYS case UNDO: if (current == first) errors = Yes; else { if (onscreen == current) reverse = Yes; current = Pred(current); undoage = Mod(last-current); } break; case REDO: if (current == last) errors = Yes; else { if (current == onscreen) reverse = No; if (history[Succ(current)].generation < history[current].generation) error(REDO_OLD); /***** Should refuse altogether??? *****/ current = Succ(current); undoage = Mod(last-current); } break; #ifdef HELPFUL case HELP: if (help()) onscreen = -1; break; #endif HELPFUL case REDRAW: onscreen = -1; trmundefined(); break; case EOF: done = Yes; break; default: Ecopy(history[current], newenv); newenv.highest = Maxintlet; newenv.changed = No; if (cmd != EXIT) errors = !execute(&newenv, cmd) || !checkep(&newenv); else done = Yes; if (errors) { switch (cmd) { case '\r': case '\n': if (newenv.mode == ATEND && !parent(newenv.focus)) { errors = !checkep(&newenv); if (!errors) done = Yes; } break; #ifdef HELPFUL case '?': if (help()) onscreen = -1; #endif HELPFUL } } if (errors) Erelease(newenv); else { #ifndef SMALLSYS if (done) done = canexit(&newenv); #endif SMALLSYS if (newenv.changed) ++newenv.generation; last = Succ(last); current = Succ(current); if (last == first) { /* Array full (always after a while). Discard "oldest". */ if (current == last || undoage < Mod(current-first)) { Erelease(history[first]); first = Succ(first); if (undoage < MAXHIST) ++undoage; } else { last = Pred(last); Erelease(history[last]); } } if (current != last && newenv.highest < history[current].highest) history[current].highest = newenv.highest; /* Move entries beyond current one up. */ for (k = last; k != current; k = Pred(k)) { if (Pred(k) == onscreen) onscreen = k; Emove(history[Pred(k)], history[k]); } Ecopy(newenv, history[current]); Erelease(history[current]); } break; /* default */ } /* switch */ if (errors && cmd != '?') { if (!slowterminal && isascii(cmd) && (isprint(cmd) || cmd == ' ')) error(INS_BAD, cmd); else error((char*)NULL); } if (savewhere) tobesaved = &history[current]; } /* for (;;) */ actupdate(Vnil, No, Yes); Erelease(*ep); Ecopy(history[current], *ep); if (savewhere) tobesaved = ep; for (current = first; current != last; current = Succ(current)) Erelease(history[current]); Erelease(history[last]); /* endshow(); */ return Yes; } /* * Execute a command, return success or failure. */ Hidden bool execute(ep, cmd) register environ *ep; register int cmd; { register bool spflag = ep->spflag; register int i; environ env; char buf[2]; register char *cp; #ifdef USERSUGG bool sugg = symbol(tree(ep->focus)) == Suggestion; #define ACCSUGG(ep) if (sugg) accsugg(ep) #define KILLSUGG(ep) if (sugg) killsugg(ep) #else !USERSUGG #define ACCSUGG(ep) /* NULL */ #define KILLSUGG(ep) /* NULL */ #endif !USERSUGG #ifdef RECORDING if (ep->newmacro && cmd != USEMACRO && cmd != SAVEMACRO) { buf[0] = cmd; buf[1] = 0; concato(&ep->newmacro, buf); } #endif RECORDING ep->spflag = No; switch (cmd) { #ifdef RECORDING case SAVEMACRO: ep->spflag = spflag; if (ep->newmacro) { /* End definition */ release(ep->oldmacro); if (ep->newmacro && Length(ep->newmacro) > 0) { ep->oldmacro = ep->newmacro; message(REC_OK); } else { release(ep->newmacro); ep->oldmacro = Vnil; } ep->newmacro = Vnil; } else /* Start definition */ ep->newmacro = mk_text(""); return Yes; case USEMACRO: if (!ep->oldmacro || Length(ep->oldmacro) <= 0) { error(PLB_NOK); return No; } ep->spflag = spflag; cp = Str(ep->oldmacro); for (i = 0; i < Length(ep->oldmacro); ++i) { Ecopy(*ep, env); if (execute(ep, cp[i]&0377) && checkep(ep)) Erelease(env); else { Erelease(*ep); Emove(env, *ep); if (!i) return No; error((char*)NULL); /* Just a bell */ /* The error must be signalled here, because the overall command (USEMACRO) succeeds, so the main loop doesn't ring the bell; but we want to inform the that not everything was done either. */ return Yes; } } return Yes; #endif RECORDING #ifndef SMALLSYS case Ctl(_): /* 'Touch', i.e. set modified flag */ ep->changed = Yes; return Yes; #endif SMALLSYS case GOTO: ACCSUGG(ep); #ifdef RECORDING if (ep->newmacro) { error(GOTO_REC); return No; } #endif RECORDING return gotocursor(ep); case NEXT: ACCSUGG(ep); return next(ep); case PREVIOUS: ACCSUGG(ep); return previous(ep); case LEFTARROW: ACCSUGG(ep); return leftarrow(ep); case RITEARROW: ACCSUGG(ep); return ritearrow(ep); case WIDEN: ACCSUGG(ep); return widen(ep); case EXTEND: ACCSUGG(ep); return extend(ep); case NARROW: ACCSUGG(ep); return narrow(ep); case RNARROW: ACCSUGG(ep); return rnarrow(ep); case UPARROW: ACCSUGG(ep); return uparrow(ep); case DOWNARROW: ACCSUGG(ep); return downarrow(ep); case UPLINE: ACCSUGG(ep); return upline(ep); case DOWNLINE: ACCSUGG(ep); return downline(ep); case COPY: ACCSUGG(ep); ep->spflag = spflag; return copyinout(ep); case DELETE: ACCSUGG(ep); return delete(ep); case ACCEPT: ACCSUGG(ep); return accept(ep); default: if (!isascii(cmd) || !isprint(cmd)) return No; ep->spflag = spflag; return ins_char(ep, cmd, islower(cmd) ? toupper(cmd) : -1); case ' ': ep->spflag = spflag; return ins_char(ep, ' ', -1); case RETURN: case NEWLINE: KILLSUGG(ep); return ins_newline(ep); } } /* * Initialize an environment variable. Most things are set to 0 or NULL. */ Hidden Procedure clrenv(ep) environ *ep; { ep->focus = newpath(Pnil, gram(Optional), 1); ep->mode = WHOLE; ep->copyflag = ep->spflag = ep->changed = No; ep->s1 = ep->s2 = ep->s3 = 0; ep->highest = Maxintlet; ep->copybuffer = Vnil; #ifdef RECORDING ep->oldmacro = ep->newmacro = Vnil; #endif RECORDING ep->generation = 0; ep->changed = No; } /* * Save parse tree and copy buffer. */ Visible Procedure enddemo() { register environ *ep = tobesaved; tobesaved = (environ*)NULL; /* To avoid loops if saving is interrupted. */ if (savewhere && ep) { if (ep->generation > 0) { save(ep->focus, savewhere); #ifdef USERSUGG writesugg(ep->focus); #endif USERSUGG } #ifdef SAVEBUF if (ep->copyflag) savequeue(ep->copybuffer, copysavefile); else savequeue(Vnil, copysavefile); #endif SAVEBUF #ifdef SAVEPOS savepos(savewhere, lineno(ep)+1); #endif SAVEPOS } #ifdef BTOP waitchild(); #endif BTOP } /* * Find out if the current position is higher in the tree * than `ever' before (as remembered in ep->highest). * The algorithm of pathlength() is repeated here to gain * some efficiency by stopping as soon as it is clear * no change can occur. * (Higher() is called VERY often, so this pays). */ Visible Procedure higher(ep) register environ *ep; { register path p = ep->focus; register int pl = 0; register int max = ep->highest; while (p) { ++pl; if (pl >= max) return; p = parent(p); } ep->highest = pl; } /* * Issue debug status message. */ Visible Procedure dbmess(ep) register environ *ep; { #ifndef SMALLSYS char stuff[80]; register string str = stuff; switch (ep->mode) { case VHOLE: sprintf(stuff, "VHOLE:%d.%d", ep->s1, ep->s2); break; case FHOLE: sprintf(stuff, "FHOLE:%d.%d", ep->s1, ep->s2); break; case ATBEGIN: str = "ATBEGIN"; break; case ATEND: str = "ATEND"; break; case WHOLE: str = "WHOLE"; break; case SUBRANGE: sprintf(stuff, "SUBRANGE:%d.%d-%d", ep->s1, ep->s2, ep->s3); break; case SUBSET: sprintf(stuff, "SUBSET:%d-%d", ep->s1, ep->s2); break; case SUBLIST: sprintf(stuff, "SUBLIST...%d", ep->s3); break; default: sprintf(stuff, "UNKNOWN:%d,%d,%d,%d", ep->mode, ep->s1, ep->s2, ep->s3); } message( #ifdef SAVEBUF "%s, %s, wi=%d, hi=%d, (y,x,l)=(%d,%d,%d) %s", symname(symbol(tree(ep->focus))), #else !SAVEBUF "%d, %s, wi=%d, hi=%d, (y,x,l)=(%d,%d,%d) %s", symbol(tree(ep->focus)), #endif SAVEBUF str, width(tree(ep->focus)), ep->highest, Ycoord(ep->focus), Xcoord(ep->focus), Level(ep->focus), ep->spflag ? "spflag on" : ""); #endif !SMALLSYS } #ifndef SMALLSYS Hidden bool canexit(ep) environ *ep; { environ env; shrink(ep); if (ishole(ep)) delete(ep); Ecopy(*ep, env); top(&ep->focus); higher(ep); ep->mode = WHOLE; if (findhole(&ep->focus)) { Erelease(env); error(EXIT_HOLES); /* There are holes left */ return No; } Erelease(*ep); Emove(env, *ep); return Yes; } Hidden bool findhole(pp) register path *pp; { register node n = tree(*pp); if (Type(n) == Tex) return No; if (symbol(n) == Hole) return Yes; if (!down(pp)) return No; for (;;) { if (findhole(pp)) return Yes; if (!rite(pp)) break; } up(pp) || Abort(); return No; } #endif !SMALLSYS