|
|
1.1 root 1: /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */
2: static char rcsid[] = "$Header: demo.c,v 2.6 85/08/22 16:01:21 timo Exp $";
3:
4: /*
5: * B editor -- Editor command processor.
6: */
7:
8: #include <ctype.h>
9:
10: #include "b.h"
11: #include "feat.h"
12: #include "erro.h"
13: #include "bobj.h"
14: #include "node.h"
15: #include "gram.h"
16: #include "keys.h"
17: #include "supr.h"
18:
19: #ifdef BTOP
20: #include <setjmp.h>
21:
22: #ifndef CMDPROMPT
23: #define CMDPROMPT ">>> " /* Prompt user for immediate command */
24: #endif CMDPROMPT
25: #endif BTOP
26:
27:
28: value editqueue();
29:
30: /* Command line flags */
31: extern bool dflag;
32: extern bool slowterminal;
33:
34: #ifdef SAVEBUF
35: extern char copysavefile[];
36: #endif SAVEBUF
37:
38:
39: Visible bool lefttorite;
40: /* Saves some time in nosuggtoqueue() for read from file */
41:
42: #define MAXHIST 101 /* One more than the number of UNDO's allowed. */
43:
44: #define Mod(k) (((k)+MAXHIST) % MAXHIST)
45: #define Succ(k) (((k)+1) % MAXHIST)
46: #define Pred(k) (((k)+MAXHIST-1) % MAXHIST)
47:
48: Hidden environ *tobesaved;
49: Hidden string savewhere;
50:
51:
52: #ifdef BTOP
53:
54: extern jmp_buf jumpback;
55: extern bool interrupted;
56: extern bool canjump;
57:
58: /*
59: * Main loop, called from main program if -t option present.
60: */
61:
62: Visible Procedure
63: mainloop()
64: {
65: environ env;
66: environ *ep = &env;
67: FILE *pdown;
68: FILE *pup;
69: int cmdchar;
70:
71: savewhere = (string)NULL;
72: tobesaved = (environ*)NULL;
73: start_b(&pdown, &pup);
74: clrenv(ep);
75: #ifdef SAVEBUF
76: ep->copybuffer = editqueue(copysavefile);
77: if (ep->copybuffer)
78: ep->copyflag = Yes;
79: #endif SAVEBUF
80:
81: for (;;) {
82: cmdchar = sleur();
83: if (cmdchar == EOF)
84: break;
85: getinput(ep, cmdchar, pdown, pup);
86: }
87: #ifdef SAVEBUF
88: if (ep->copyflag)
89: savequeue(ep->copybuffer, copysavefile);
90: else
91: savequeue(Vnil, copysavefile);
92: #endif SAVEBUF
93: Erelease(*ep);
94: }
95:
96:
97: /*
98: * Provide input for the interpreter.
99: */
100:
101: Hidden Procedure
102: getinput(ep, cmdchar, pdown, pup)
103: environ *ep;
104: int cmdchar;
105: FILE *pdown;
106: FILE *pup;
107: {
108: int n;
109: char buffer[100];
110: char filename[100];
111: int lineno;
112:
113:
114: switch (cmdchar) {
115:
116: case '>': /* Immediate command */
117: case 'E': /* Expression */
118: case 'R': /* Raw input */
119: case 'Y': /* Yes/No */
120: if (cmdchar == '>')
121: setroot("Imm_cmd");
122: else if (cmdchar == 'E')
123: setroot("Expression");
124: else
125: setroot("Raw_input");
126: delfocus(&ep->focus);
127: initshow();
128: if (cmdchar == '>')
129: cmdprompt(CMDPROMPT);
130: editdocument(ep);
131: endshow();
132: top(&ep->focus);
133: ep->mode = WHOLE;
134: if (!interrupted)
135: send(ep->focus, pdown);
136: delete(ep);
137: break;
138:
139: case ':':
140: case '=':
141: fgets(buffer, sizeof buffer, pup);
142: if (index(buffer, '+'))
143: n = sscanf(buffer, " +%d %s", &lineno, filename) - 1;
144: else {
145: n = sscanf(buffer, " %s", filename);
146: lineno = 0;
147: }
148: if (n == 1) {
149: initshow();
150: dofile(ep, filename, lineno);
151: endshow();
152: top(&ep->focus);
153: ep->mode = WHOLE;
154: delete(ep);
155: if (!ep->copyflag) {
156: release(ep->copybuffer);
157: ep->copybuffer = Vnil;
158: }
159: }
160: putc('\n', pdown);
161: interrupted = No; /* Interrupts handled locally in editdocument! */
162: break;
163:
164: default:
165: printf("[Unrecognized command character '%c' (0%o)]\n",
166: cmdchar&0177, cmdchar);
167:
168: }
169: }
170:
171: #endif BTOP
172:
173:
174: #ifdef FILEARGS
175:
176: /*
177: * Edit a single unit or target, called from main program if file name
178: * arguments are present.
179: */
180:
181: Visible Procedure
182: demo(filename, linenumber)
183: string filename;
184: int linenumber;
185: {
186: environ env;
187: environ *ep = &env;
188: bool ok;
189:
190: clrenv(ep);
191: #ifdef SAVEBUF
192: ep->copybuffer = editqueue(copysavefile);
193: if (ep->copybuffer)
194: ep->copyflag = Yes;
195: #endif SAVEBUF
196: initshow();
197: ok = dofile(ep, filename, linenumber);
198: endshow();
199: if (!ok)
200: return No;
201: #ifdef SAVEBUF
202: if (ep->copyflag)
203: savequeue(ep->copybuffer, copysavefile);
204: else
205: savequeue(Vnil, copysavefile);
206: #endif SAVEBUF
207: Erelease(*ep);
208: return Yes;
209: }
210:
211: #endif !FILEARGS
212:
213:
214: /*
215: * Edit a unit or target, using the environment offered as a parameter.
216: */
217:
218: Hidden bool
219: dofile(ep, filename, linenumber)
220: environ *ep;
221: string filename;
222: int linenumber;
223: {
224: #ifdef HELPFUL
225: static bool didmessage;
226:
227: if (!didmessage) {
228: didmessage = Yes;
229: message("[Press ? or ESC-? for help]");
230: }
231: #endif HELPFUL
232: #ifdef SAVEPOS
233: if (linenumber <= 0)
234: linenumber = getpos(filename);
235: #endif SAVEPOS
236: setroot(filename[0] == '=' ? "Target_edit" : "Unit_edit");
237: savewhere = filename;
238: tobesaved = (environ*)NULL;
239:
240: lefttorite = Yes;
241: edit(ep, filename, linenumber);
242: #ifdef USERSUGG
243: readsugg(ep->focus);
244: #endif USERSUGG
245: lefttorite = No;
246:
247: ep->generation = 0;
248: if (!editdocument(ep))
249: return No;
250: if (ep->generation > 0) {
251: if (!save(ep->focus, filename))
252: error("Cannot save unit: %s", unixerror(filename));
253: #ifdef USERSUGG
254: writesugg(ep->focus);
255: #endif USERSUGG
256: }
257: #ifdef SAVEPOS
258: savepos(filename, lineno(ep)+1);
259: #endif SAVEPOS
260: savewhere = (char*)NULL;
261: tobesaved = (environ*)NULL;
262: return Yes;
263: }
264:
265:
266: /*
267: * Call the editor for a given document.
268: */
269:
270: Hidden bool
271: editdocument(ep)
272: environ *ep;
273: {
274: int k;
275: int first = 0;
276: int last = 0;
277: int current = 0;
278: int onscreen = -1;
279: bool reverse = No;
280: environ newenv;
281: int cmd;
282: bool errors = No;
283: int undoage = 0;
284: bool done = No;
285: environ history[MAXHIST];
286:
287: Ecopy(*ep, history[0]);
288:
289: for (;;) { /* Command interpretation loop */
290: if (onscreen != current)
291: virtupdate(onscreen < 0 ? (environ*)NULL : &history[onscreen],
292: &history[current],
293: reverse && onscreen >= 0 ?
294: history[onscreen].highest : history[current].highest);
295: onscreen = current;
296: if (done)
297: break;
298: #ifdef BTOP
299: if (!interrupted && !moreinput())
300: #else BTOP
301: if (!moreinput())
302: #endif BTOP
303: actupdate(history[current].copyflag ?
304: history[current].copybuffer : Vnil,
305: #ifdef RECORDING
306: history[current].newmacro != Vnil,
307: #else !RECORDING
308: No,
309: #endif !RECORDING
310: No);
311: #ifdef BTOP
312: if (interrupted || setjmp(jumpback))
313: break;
314: canjump = Yes;
315: #endif BTOP
316: cmd = inchar();
317: #ifdef BTOP
318: canjump = No;
319: #endif BTOP
320: errors = No;
321:
322: switch (cmd) {
323:
324: #ifndef NDEBUG
325: case Ctl(@): /* Debug exit with variable dump */
326: tobesaved = (environ*)NULL;
327: return No;
328: #endif NDEBUG
329:
330: #ifndef SMALLSYS
331: case Ctl(^): /* Debug status message */
332: dbmess(&history[current]);
333: errors = Yes; /* Causes clear after new keystroke seen */
334: continue;
335: #endif !SMALLSYS
336:
337: case UNDO:
338: if (current == first)
339: errors = Yes;
340: else {
341: if (onscreen == current)
342: reverse = Yes;
343: current = Pred(current);
344: undoage = Mod(last-current);
345: }
346: break;
347:
348: case REDO:
349: if (current == last)
350: errors = Yes;
351: else {
352: if (current == onscreen)
353: reverse = No;
354: if (history[Succ(current)].generation <
355: history[current].generation)
356: error(REDO_OLD); /***** Should refuse altogether??? *****/
357: current = Succ(current);
358: undoage = Mod(last-current);
359: }
360: break;
361:
362: #ifdef HELPFUL
363: case HELP:
364: if (help())
365: onscreen = -1;
366: break;
367: #endif HELPFUL
368:
369: case REDRAW:
370: onscreen = -1;
371: trmundefined();
372: break;
373:
374: case EOF:
375: done = Yes;
376: break;
377:
378: default:
379: Ecopy(history[current], newenv);
380: newenv.highest = Maxintlet;
381: newenv.changed = No;
382: if (cmd != EXIT)
383: errors = !execute(&newenv, cmd) || !checkep(&newenv);
384: else
385: done = Yes;
386: if (errors) {
387: switch (cmd) {
388: case '\r':
389: case '\n':
390: if (newenv.mode == ATEND && !parent(newenv.focus)) {
391: errors = !checkep(&newenv);
392: if (!errors)
393: done = Yes;
394: }
395: break;
396: #ifdef HELPFUL
397: case '?':
398: if (help())
399: onscreen = -1;
400: #endif HELPFUL
401: }
402: }
403: if (errors)
404: Erelease(newenv);
405: else {
406: #ifndef SMALLSYS
407: if (done)
408: done = canexit(&newenv);
409: #endif SMALLSYS
410: if (newenv.changed)
411: ++newenv.generation;
412: last = Succ(last);
413: current = Succ(current);
414: if (last == first) {
415: /* Array full (always after a while). Discard "oldest". */
416: if (current == last
417: || undoage < Mod(current-first)) {
418: Erelease(history[first]);
419: first = Succ(first);
420: if (undoage < MAXHIST)
421: ++undoage;
422: }
423: else {
424: last = Pred(last);
425: Erelease(history[last]);
426: }
427: }
428: if (current != last
429: && newenv.highest < history[current].highest)
430: history[current].highest = newenv.highest;
431: /* Move entries beyond current one up. */
432: for (k = last; k != current; k = Pred(k)) {
433: if (Pred(k) == onscreen)
434: onscreen = k;
435: Emove(history[Pred(k)], history[k]);
436: }
437: Ecopy(newenv, history[current]);
438: Erelease(history[current]);
439: }
440: break; /* default */
441:
442: } /* switch */
443:
444: if (errors && cmd != '?') {
445: if (!slowterminal && isascii(cmd)
446: && (isprint(cmd) || cmd == ' '))
447: error(INS_BAD, cmd);
448: else
449: error((char*)NULL);
450: }
451: if (savewhere)
452: tobesaved = &history[current];
453: } /* for (;;) */
454:
455: actupdate(Vnil, No, Yes);
456: Erelease(*ep);
457: Ecopy(history[current], *ep);
458: if (savewhere)
459: tobesaved = ep;
460: for (current = first; current != last; current = Succ(current))
461: Erelease(history[current]);
462: Erelease(history[last]);
463: /* endshow(); */
464: return Yes;
465: }
466:
467:
468: /*
469: * Execute a command, return success or failure.
470: */
471:
472: Hidden bool
473: execute(ep, cmd)
474: register environ *ep;
475: register int cmd;
476: {
477: register bool spflag = ep->spflag;
478: register int i;
479: environ env;
480: char buf[2];
481: register char *cp;
482: #ifdef USERSUGG
483: bool sugg = symbol(tree(ep->focus)) == Suggestion;
484: #define ACCSUGG(ep) if (sugg) accsugg(ep)
485: #define KILLSUGG(ep) if (sugg) killsugg(ep)
486: #else !USERSUGG
487: #define ACCSUGG(ep) /* NULL */
488: #define KILLSUGG(ep) /* NULL */
489: #endif !USERSUGG
490:
491: #ifdef RECORDING
492: if (ep->newmacro && cmd != USEMACRO && cmd != SAVEMACRO) {
493: buf[0] = cmd;
494: buf[1] = 0;
495: concato(&ep->newmacro, buf);
496: }
497: #endif RECORDING
498: ep->spflag = No;
499:
500: switch (cmd) {
501:
502: #ifdef RECORDING
503: case SAVEMACRO:
504: ep->spflag = spflag;
505: if (ep->newmacro) { /* End definition */
506: release(ep->oldmacro);
507: if (ep->newmacro && Length(ep->newmacro) > 0) {
508: ep->oldmacro = ep->newmacro;
509: message(REC_OK);
510: }
511: else {
512: release(ep->newmacro);
513: ep->oldmacro = Vnil;
514: }
515: ep->newmacro = Vnil;
516: }
517: else /* Start definition */
518: ep->newmacro = mk_text("");
519: return Yes;
520:
521: case USEMACRO:
522: if (!ep->oldmacro || Length(ep->oldmacro) <= 0) {
523: error(PLB_NOK);
524: return No;
525: }
526: ep->spflag = spflag;
527: cp = Str(ep->oldmacro);
528: for (i = 0; i < Length(ep->oldmacro); ++i) {
529: Ecopy(*ep, env);
530: if (execute(ep, cp[i]&0377) && checkep(ep))
531: Erelease(env);
532: else {
533: Erelease(*ep);
534: Emove(env, *ep);
535: if (!i)
536: return No;
537: error((char*)NULL); /* Just a bell */
538: /* The error must be signalled here, because the overall
539: command (USEMACRO) succeeds, so the main loop
540: doesn't ring the bell; but we want to inform the
541: that not everything was done either. */
542: return Yes;
543: }
544: }
545: return Yes;
546: #endif RECORDING
547:
548: #ifndef SMALLSYS
549: case Ctl(_): /* 'Touch', i.e. set modified flag */
550: ep->changed = Yes;
551: return Yes;
552: #endif SMALLSYS
553:
554: case GOTO:
555: ACCSUGG(ep);
556: #ifdef RECORDING
557: if (ep->newmacro) {
558: error(GOTO_REC);
559: return No;
560: }
561: #endif RECORDING
562: return gotocursor(ep);
563:
564: case NEXT:
565: ACCSUGG(ep);
566: return next(ep);
567:
568: case PREVIOUS:
569: ACCSUGG(ep);
570: return previous(ep);
571:
572: case LEFTARROW:
573: ACCSUGG(ep);
574: return leftarrow(ep);
575:
576: case RITEARROW:
577: ACCSUGG(ep);
578: return ritearrow(ep);
579:
580: case WIDEN:
581: ACCSUGG(ep);
582: return widen(ep);
583:
584: case EXTEND:
585: ACCSUGG(ep);
586: return extend(ep);
587:
588: case NARROW:
589: ACCSUGG(ep);
590: return narrow(ep);
591:
592: case RNARROW:
593: ACCSUGG(ep);
594: return rnarrow(ep);
595:
596: case UPARROW:
597: ACCSUGG(ep);
598: return uparrow(ep);
599:
600: case DOWNARROW:
601: ACCSUGG(ep);
602: return downarrow(ep);
603:
604: case UPLINE:
605: ACCSUGG(ep);
606: return upline(ep);
607:
608: case DOWNLINE:
609: ACCSUGG(ep);
610: return downline(ep);
611:
612: case COPY:
613: ACCSUGG(ep);
614: ep->spflag = spflag;
615: return copyinout(ep);
616:
617: case DELETE:
618: ACCSUGG(ep);
619: return delete(ep);
620:
621: case ACCEPT:
622: ACCSUGG(ep);
623: return accept(ep);
624:
625: default:
626: if (!isascii(cmd) || !isprint(cmd))
627: return No;
628: ep->spflag = spflag;
629: return ins_char(ep, cmd, islower(cmd) ? toupper(cmd) : -1);
630:
631: case ' ':
632: ep->spflag = spflag;
633: return ins_char(ep, ' ', -1);
634:
635: case RETURN:
636: case NEWLINE:
637: KILLSUGG(ep);
638: return ins_newline(ep);
639: }
640: }
641:
642:
643: /*
644: * Initialize an environment variable. Most things are set to 0 or NULL.
645: */
646:
647: Hidden Procedure
648: clrenv(ep)
649: environ *ep;
650: {
651: ep->focus = newpath(Pnil, gram(Optional), 1);
652: ep->mode = WHOLE;
653: ep->copyflag = ep->spflag = ep->changed = No;
654: ep->s1 = ep->s2 = ep->s3 = 0;
655: ep->highest = Maxintlet;
656: ep->copybuffer = Vnil;
657: #ifdef RECORDING
658: ep->oldmacro = ep->newmacro = Vnil;
659: #endif RECORDING
660: ep->generation = 0;
661: ep->changed = No;
662: }
663:
664:
665: /*
666: * Save parse tree and copy buffer.
667: */
668:
669: Visible Procedure
670: enddemo()
671: {
672: register environ *ep = tobesaved;
673:
674: tobesaved = (environ*)NULL;
675: /* To avoid loops if saving is interrupted. */
676: if (savewhere && ep) {
677: if (ep->generation > 0) {
678: save(ep->focus, savewhere);
679: #ifdef USERSUGG
680: writesugg(ep->focus);
681: #endif USERSUGG
682: }
683: #ifdef SAVEBUF
684: if (ep->copyflag)
685: savequeue(ep->copybuffer, copysavefile);
686: else
687: savequeue(Vnil, copysavefile);
688: #endif SAVEBUF
689: #ifdef SAVEPOS
690: savepos(savewhere, lineno(ep)+1);
691: #endif SAVEPOS
692: }
693: #ifdef BTOP
694: waitchild();
695: #endif BTOP
696: }
697:
698:
699: /*
700: * Find out if the current position is higher in the tree
701: * than `ever' before (as remembered in ep->highest).
702: * The algorithm of pathlength() is repeated here to gain
703: * some efficiency by stopping as soon as it is clear
704: * no change can occur.
705: * (Higher() is called VERY often, so this pays).
706: */
707:
708: Visible Procedure
709: higher(ep)
710: register environ *ep;
711: {
712: register path p = ep->focus;
713: register int pl = 0;
714: register int max = ep->highest;
715:
716: while (p) {
717: ++pl;
718: if (pl >= max)
719: return;
720: p = parent(p);
721: }
722: ep->highest = pl;
723: }
724:
725:
726: /*
727: * Issue debug status message.
728: */
729:
730: Visible Procedure
731: dbmess(ep)
732: register environ *ep;
733: {
734: #ifndef SMALLSYS
735: char stuff[80];
736: register string str = stuff;
737:
738: switch (ep->mode) {
739: case VHOLE:
740: sprintf(stuff, "VHOLE:%d.%d", ep->s1, ep->s2);
741: break;
742: case FHOLE:
743: sprintf(stuff, "FHOLE:%d.%d", ep->s1, ep->s2);
744: break;
745: case ATBEGIN:
746: str = "ATBEGIN";
747: break;
748: case ATEND:
749: str = "ATEND";
750: break;
751: case WHOLE:
752: str = "WHOLE";
753: break;
754: case SUBRANGE:
755: sprintf(stuff, "SUBRANGE:%d.%d-%d", ep->s1, ep->s2, ep->s3);
756: break;
757: case SUBSET:
758: sprintf(stuff, "SUBSET:%d-%d", ep->s1, ep->s2);
759: break;
760: case SUBLIST:
761: sprintf(stuff, "SUBLIST...%d", ep->s3);
762: break;
763: default:
764: sprintf(stuff, "UNKNOWN:%d,%d,%d,%d",
765: ep->mode, ep->s1, ep->s2, ep->s3);
766: }
767: message(
768: #ifdef SAVEBUF
769: "%s, %s, wi=%d, hi=%d, (y,x,l)=(%d,%d,%d) %s",
770: symname(symbol(tree(ep->focus))),
771: #else !SAVEBUF
772: "%d, %s, wi=%d, hi=%d, (y,x,l)=(%d,%d,%d) %s",
773: symbol(tree(ep->focus)),
774: #endif SAVEBUF
775: str, width(tree(ep->focus)), ep->highest,
776: Ycoord(ep->focus), Xcoord(ep->focus), Level(ep->focus),
777: ep->spflag ? "spflag on" : "");
778: #endif !SMALLSYS
779: }
780:
781: #ifndef SMALLSYS
782:
783: Hidden bool
784: canexit(ep)
785: environ *ep;
786: {
787: environ env;
788:
789: shrink(ep);
790: if (ishole(ep))
791: delete(ep);
792: Ecopy(*ep, env);
793: top(&ep->focus);
794: higher(ep);
795: ep->mode = WHOLE;
796: if (findhole(&ep->focus)) {
797: Erelease(env);
798: error(EXIT_HOLES); /* There are holes left */
799: return No;
800: }
801: Erelease(*ep);
802: Emove(env, *ep);
803: return Yes;
804: }
805:
806:
807: Hidden bool
808: findhole(pp)
809: register path *pp;
810: {
811: register node n = tree(*pp);
812:
813: if (Type(n) == Tex)
814: return No;
815: if (symbol(n) == Hole)
816: return Yes;
817: if (!down(pp))
818: return No;
819: for (;;) {
820: if (findhole(pp))
821: return Yes;
822: if (!rite(pp))
823: break;
824:
825: }
826: up(pp) || Abort();
827: return No;
828: }
829:
830: #endif !SMALLSYS
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.