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