1: /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */
   2: static char rcsid[] = "$Header: deco.c,v 2.3 84/07/19 11:45:12 guido Exp $";
   3: 
   4: /*
   5:  * B editor -- Delete and copy commands.
   6:  */
   7: 
   8: #include <ctype.h>
   9: 
  10: #include "b.h"
  11: #include "erro.h"
  12: #include "bobj.h"
  13: #include "node.h"
  14: #include "gram.h"
  15: #include "supr.h"
  16: #include "queu.h"
  17: 
  18: 
  19: value copyout(); /* Forward */
  20: 
  21: /*
  22:  * DELETE and COPY currently share a buffer, called the copy buffer.
  23:  * (Physically, there is one such a buffer in each environment.)
  24:  * In ordinary use, the copy buffer receives the text deleted by the
  25:  * last DELETE command (unless it just removed a hole); the COPY command
  26:  * can then be used (with the focus on a hole) to copy it back.
  27:  * When some portion of text must be held while other text is deleted,
  28:  * the COPY command again, but now with the focus on the text to be held,
  29:  * copies it to the buffer and deleted text won't overwrite the buffer
  30:  * until it is copied back at least once.
  31:  * If the buffer holds text that was explicitly copied out but not yet
  32:  * copied back in, it is saved on a file when the editor exits, so it can
  33:  * be used in the next session; but this is not true for text implicitly
  34:  * placed in the buffer through DELETE.
  35:  */
  36: 
  37: /*
  38:  * Delete command -- delete the text in the focus, or delete the hole
  39:  * if it is only a hole.
  40:  */
  41: 
  42: Visible bool
  43: delete(ep)
  44:     register environ *ep;
  45: {
  46:     higher(ep);
  47:     shrink(ep);
  48:     if (ishole(ep))
  49:         return delhole(ep);
  50:     if (!ep->copyflag) {
  51:         release(ep->copybuffer);
  52:         ep->copybuffer = copyout(ep);
  53:     }
  54:     return delbody(ep);
  55: }
  56: 
  57: 
  58: /*
  59:  * Delete the focus under the assumption that it contains some text.
  60:  */
  61: 
  62: Visible bool
  63: delbody(ep)
  64:     register environ *ep;
  65: {
  66:     ep->changed = Yes;
  67: 
  68:     subgrow(ep, No); /* Don't ignore spaces */
  69:     switch (ep->mode) {
  70: 
  71:     case SUBRANGE:
  72:         if (ep->s1&1)
  73:             return delfixed(ep);
  74:         return delvarying(ep);
  75: 
  76:     case SUBSET:
  77:         return delsubset(ep, Yes);
  78: 
  79:     case SUBLIST:
  80:         return delsublist(ep);
  81: 
  82:     case WHOLE:
  83:         return delwhole(ep);
  84: 
  85:     default:
  86:         Abort();
  87:         /* NOTREACHED */
  88:     }
  89: }
  90: 
  91: 
  92: /*
  93:  * Delete portion (ep->mode == SUBRANGE) of varying text ((ep->s1&1) == 0).
  94:  */
  95: 
  96: Hidden bool
  97: delvarying(ep)
  98:     register environ *ep;
  99: {
 100:     auto queue q = Qnil;
 101:     register node n = tree(ep->focus);
 102:     auto value v = (value) child(n, ep->s1/2);
 103:     register len = Length(v);
 104: 
 105:     Assert(ep->mode == SUBRANGE && !(ep->s1&1)); /* Wrong call */
 106:     Assert(Type(v) == Tex); /* Inconsistent parse tree */
 107:     if (ep->s2 == 0
 108:         && !mayinsert(tree(ep->focus), ep->s1/2, 0, Str(v)[ep->s3 + 1])) {
 109:         /* Cannot do simple substring deletion. */
 110:         stringtoqueue(Str(v) + ep->s3 + 1, &q);
 111:         delfocus(&ep->focus);
 112:         ep->mode = WHOLE;
 113:         return app_queue(ep, &q);
 114:     }
 115:     v = copy(v);
 116:     putintrim(&v, ep->s2, len - ep->s3 - 1, "");
 117:     s_downi(ep, ep->s1/2);
 118:     replace(&ep->focus, (node) v);
 119:     s_up(ep);
 120:     ep->mode = VHOLE;
 121:     return Yes;
 122: }
 123: 
 124: 
 125: /*
 126:  * Delete portion (ep->mode == SUBRANGE) of fixed text ((ep->s1&1) == 1).
 127:  */
 128: 
 129: Hidden bool
 130: delfixed(ep)
 131:     register environ *ep;
 132: {
 133:     register node n = tree(ep->focus);
 134:     char buf[15]; /* Long enough for all fixed texts */
 135:     register string repr = noderepr(n)[ep->s1/2];
 136:     register int len;
 137:     queue q = Qnil;
 138:     bool ok;
 139: 
 140:     Assert(ep->mode == SUBRANGE && (ep->s1&1));
 141:     if (ep->s1 > 1) {
 142:         ep->mode = FHOLE;
 143:         return Yes;
 144:     }
 145:     Assert(fwidth(repr) < sizeof buf - 1);
 146:     len = ep->s2;
 147:     ep->s2 = ep->s3 + 1;
 148:     ep->mode = FHOLE;
 149:     nosuggtoqueue(ep, &q);
 150:     strcpy(buf, repr);
 151:     if (nchildren(tree(ep->focus)) > 0)
 152:         buf[len] = 0;
 153:     else
 154:         strcpy(buf+len, buf+ep->s2);
 155:     delfocus(&ep->focus);
 156:     ep->mode = WHOLE;
 157:     markpath(&ep->focus, 1);
 158:     ok = ins_string(ep, buf, &q, 0);
 159:     if (!ok) {
 160:         qrelease(q);
 161:         return No;
 162:     }
 163:     firstmarked(&ep->focus, 1) || Abort();
 164:     unmkpath(&ep->focus, 1);
 165:     fixfocus(ep, len);
 166:     return app_queue(ep, &q);
 167: }
 168: 
 169: 
 170: /*
 171:  * Delete focus if ep->mode == SUBSET.
 172:  */
 173: 
 174: Hidden bool
 175: delsubset(ep, hack)
 176:     register environ *ep;
 177:     bool hack;
 178: {
 179:     auto queue q = Qnil;
 180:     auto queue q2 = Qnil;
 181:     register node n = tree(ep->focus);
 182:     register node nn;
 183:     register string *rp = noderepr(n);
 184:     register int nch = nchildren(n);
 185:     register int i;
 186: 
 187:     if (hack) {
 188:         shrsubset(ep);
 189:         if (ep->s1 == ep->s2 && !(ep->s1&1)) {
 190:             nn = child(tree(ep->focus), ep->s1/2);
 191:             if (fwidth(noderepr(nn)[0]) < 0) {
 192:                 /* It starts with a newline, leave the newline */
 193:                 s_downi(ep, ep->s1/2);
 194:                 ep->mode = SUBSET;
 195:                 ep->s1 = 2;
 196:                 ep->s2 = 2*nchildren(nn) + 1;
 197:                 return delsubset(ep, hack);
 198:             }
 199:         }
 200:         subgrsubset(ep, No); /* Undo shrsubset */
 201:         if (ep->s2 == 3 && rp[1] && Strequ(rp[1], "\t"))
 202:             --ep->s2; /* Hack for deletion of unit-head or if/for/wh. head */
 203:     }
 204:     if (ep->s1 == 1 && Fw_negative(rp[0]))
 205:         ++ep->s1; /* Hack for deletion of test-suite or refinement head */
 206: 
 207:     if (Fw_zero(rp[0]) ? (ep->s2 < 3 || ep->s1 > 3) : ep->s1 > 1) {
 208:         /* No deep structural change */
 209:         for (i = (ep->s1+1)/2; i <= ep->s2/2; ++i) {
 210:             s_downi(ep, i);
 211:             delfocus(&ep->focus);
 212:             s_up(ep);
 213:         }
 214:         if (ep->s1&1) {
 215:             ep->mode = FHOLE;
 216:             ep->s2 = 0;
 217:         }
 218:         else if (Type(child(tree(ep->focus), ep->s1/2)) == Tex) {
 219:             ep->mode = VHOLE;
 220:             ep->s2 = 0;
 221:         }
 222:         else {
 223:             s_downi(ep, ep->s1/2);
 224:             ep->mode = ATBEGIN;
 225:         }
 226:         return Yes;
 227:     }
 228: 
 229:     balance(ep); /* Make balanced \t - \b pairs */
 230:     subsettoqueue(n, 1, ep->s1-1, &q);
 231:     subsettoqueue(n, ep->s2+1, 2*nch+1, &q2);
 232:     nonewline(&q2); /* Wonder what will happen...? */
 233:     delfocus(&ep->focus);
 234:     ep->mode = ATBEGIN;
 235:     leftvhole(ep);
 236:     if (!ins_queue(ep, &q, &q2)) {
 237:         qrelease(q2);
 238:         return No;
 239:     }
 240:     return app_queue(ep, &q2);
 241: }
 242: 
 243: 
 244: /*
 245:  * Delete the focus if ep->mode == SUBLIST.
 246:  */
 247: 
 248: delsublist(ep)
 249:     register environ *ep;
 250: {
 251:     register node n;
 252:     register int i;
 253:     register int sym;
 254:     queue q = Qnil;
 255:     bool flag;
 256: 
 257:     Assert(ep->mode == SUBLIST);
 258:     n = tree(ep->focus);
 259:     flag = fwidth(noderepr(n)[0]) < 0;
 260:     for (i = ep->s3; i > 0; --i) {
 261:         n = lastchild(n);
 262:         Assert(n);
 263:     }
 264:     if (flag) {
 265:         n = nodecopy(n);
 266:         s_down(ep);
 267:         do {
 268:             delfocus(&ep->focus);
 269:         } while (rite(&ep->focus));
 270:         if (!allowed(ep->focus, symbol(n))) {
 271:             error(DEL_REM); /* The remains wouldn't fit */
 272:             noderelease(n);
 273:             return No;
 274:         }
 275:         replace(&ep->focus, n);
 276:         s_up(ep);
 277:         s_down(ep); /* I.e., to leftmost sibling */
 278:         ep->mode = WHOLE;
 279:         return Yes;
 280:     }
 281:     sym = symbol(n);
 282:     if (sym == Optional || sym == Hole) {
 283:         delfocus(&ep->focus);
 284:         ep->mode = WHOLE;
 285:     }
 286:     else if (!allowed(ep->focus, sym)) {
 287:         preptoqueue(n, &q);
 288:         delfocus(&ep->focus);
 289:         ep->mode = WHOLE;
 290:         return app_queue(ep, &q);
 291:     }
 292:     else {
 293:         replace(&ep->focus, nodecopy(n));
 294:         ep->mode = ATBEGIN;
 295:     }
 296:     return Yes;
 297: }
 298: 
 299: 
 300: /*
 301:  * Delete the focus if ep->mode == WHOLE.
 302:  */
 303: 
 304: Hidden bool
 305: delwhole(ep)
 306:     register environ *ep;
 307: {
 308:     register int sym = symbol(tree(ep->focus));
 309: 
 310:     Assert(ep->mode == WHOLE);
 311:     if (sym == Optional || sym == Hole)
 312:         return No;
 313:     delfocus(&ep->focus);
 314:     return Yes;
 315: }
 316: 
 317: 
 318: /*
 319:  * Delete the focus if it is only a hole.
 320:  * Assume shrink() has been called before!
 321:  */
 322: 
 323: Hidden bool
 324: delhole(ep)
 325:     register environ *ep;
 326: {
 327:     node n;
 328:     int sym;
 329:     bool flag = No;
 330: 
 331:     switch (ep->mode) {
 332: 
 333:     case ATBEGIN:
 334:     case VHOLE:
 335:     case FHOLE:
 336:     case ATEND:
 337:         return widen(ep);
 338: 
 339:     case WHOLE:
 340:         Assert((sym = symbol(tree(ep->focus))) == Optional || sym == Hole);
 341:         if (ichild(ep->focus) != 1)
 342:             break;
 343:         if (!up(&ep->focus))
 344:             return No;
 345:         higher(ep);
 346:         ep->mode = SUBSET;
 347:         ep->s1 = 2;
 348:         ep->s2 = 2;
 349:         if (fwidth(noderepr(tree(ep->focus))[0]) < 0) {
 350:             flag = Yes;
 351:             ep->s2 = 3; /* Extend to rest of line */
 352:         }
 353:     }
 354: 
 355:     ep->changed = Yes;
 356:     grow(ep);
 357: 
 358:     switch (ep->mode) {
 359: 
 360:     case SUBSET:
 361:         if (!delsubset(ep, No))
 362:             return No;
 363:         if (!flag)
 364:             return widen(ep);
 365:         leftvhole(ep);
 366:         oneline(ep);
 367:         return Yes;
 368: 
 369:     case SUBLIST:
 370:         n = tree(ep->focus);
 371:         n = lastchild(n);
 372:         sym = symbol(n);
 373:         if (!allowed(ep->focus, sym)) {
 374:             error(DEL_REM); /* The remains wouldn't fit */
 375:             return No;
 376:         }
 377:         flag = samelevel(sym, symbol(tree(ep->focus)));
 378:         replace(&ep->focus, nodecopy(n));
 379:         if (flag) {
 380:             ep->mode = SUBLIST;
 381:             ep->s3 = 1;
 382:         }
 383:         else
 384:             ep->mode = WHOLE;
 385:         return Yes;
 386: 
 387:     case WHOLE:
 388:         Assert(!parent(ep->focus)); /* Must be at root! */
 389:         return No;
 390: 
 391:     default:
 392:         Abort();
 393:         /* NOTREACHED */
 394: 
 395:     }
 396: }
 397: 
 398: 
 399: /*
 400:  * Subroutine to delete the focus.
 401:  */
 402: 
 403: Visible Procedure
 404: delfocus(pp)
 405:     register path *pp;
 406: {
 407:     register path pa = parent(*pp);
 408:     register int sympa = pa ? symbol(tree(pa)) : Rootsymbol;
 409: 
 410:     replace(pp, child(gram(sympa), ichild(*pp)));
 411: }
 412: 
 413: 
 414: /*
 415:  * Copy command -- copy the focus to the copy buffer if it contains
 416:  * some text, copy the copy buffer into the focus if the focus is
 417:  * empty (just a hole).
 418:  */
 419: 
 420: Visible bool
 421: copyinout(ep)
 422:     register environ *ep;
 423: {
 424:     shrink(ep);
 425:     if (!ishole(ep)) {
 426:         release(ep->copybuffer);
 427:         ep->copybuffer = copyout(ep);
 428:         ep->copyflag = !!ep->copybuffer;
 429:         return ep->copyflag;
 430:     }
 431:     else {
 432:         fixit(ep); /* Make sure it looks like a hole now */
 433:         if (!copyin(ep, (queue) ep->copybuffer))
 434:             return No;
 435:         ep->copyflag = No;
 436:         return Yes;
 437:     }
 438: }
 439: 
 440: 
 441: /*
 442:  * Copy the focus to the copy buffer.
 443:  */
 444: 
 445: Visible value
 446: copyout(ep)
 447:     register environ *ep;
 448: {
 449:     auto queue q = Qnil;
 450:     auto path p;
 451:     register node n;
 452:     register value v;
 453:     char buf[15];
 454:     register string *rp;
 455:     register int i;
 456: 
 457:     switch (ep->mode) {
 458:     case WHOLE:
 459:         preptoqueue(tree(ep->focus), &q);
 460:         break;
 461:     case SUBLIST:
 462:         p = pathcopy(ep->focus);
 463:         for (i = ep->s3; i > 0; --i)
 464:             downrite(&p) || Abort();
 465:         for (i = ep->s3; i > 0; --i) {
 466:             up(&p) || Abort();
 467:             n = tree(p);
 468:             subsettoqueue(n, 1, 2*nchildren(n) - 1, &q);
 469:         }
 470:         pathrelease(p);
 471:         break;
 472:     case SUBSET:
 473:         balance(ep);
 474:         subsettoqueue(tree(ep->focus), ep->s1, ep->s2, &q);
 475:         break;
 476:     case SUBRANGE:
 477:         Assert(ep->s3 >= ep->s2);
 478:         if (ep->s1&1) { /* Fixed text */
 479:             Assert(ep->s3 - ep->s2 + 1 < sizeof buf);
 480:             rp = noderepr(tree(ep->focus));
 481:             Assert(ep->s2 < Fwidth(rp[ep->s1/2]));
 482:             strncpy(buf, rp[ep->s1/2] + ep->s2, ep->s3 - ep->s2 + 1);
 483:             buf[ep->s3 - ep->s2 + 1] = 0;
 484:             stringtoqueue(buf, &q);
 485:         }
 486:         else { /* Varying text */
 487:             v = (value) child(tree(ep->focus), ep->s1/2);
 488:             Assert(Type(v) == Tex);
 489:             v = trim(v, ep->s2, Length(v) - ep->s3 - 1);
 490:             preptoqueue((node)v, &q);
 491:             release(v);
 492:         }
 493:         break;
 494:     default:
 495:         Abort();
 496:     }
 497:     nonewline(&q);
 498:     return (value)q;
 499: }
 500: 
 501: 
 502: /*
 503:  * Subroutine to ensure the copy buffer doesn't start with a newline.
 504:  */
 505: 
 506: Hidden Procedure
 507: nonewline(pq)
 508:     register queue *pq;
 509: {
 510:     register node n;
 511:     register int c;
 512: 
 513:     if (!emptyqueue(*pq)) {
 514:         for (;;) {
 515:             n = queuebehead(pq);
 516:             if (Type(n) == Tex) {
 517:                 if (Str((value) n)[0] != '\n')
 518:                     preptoqueue(n, pq);
 519:                 noderelease(n);
 520:                 break;
 521:             }
 522:             else {
 523:                 c = nodechar(n);
 524:                 if (c != '\n')
 525:                     preptoqueue(n, pq);
 526:                 else
 527:                     splitnode(n, pq);
 528:                 noderelease(n);
 529:                 if (c != '\n')
 530:                     break;
 531:             }
 532:         }
 533:     }
 534: }
 535: 
 536: 
 537: /*
 538:  * Refinement for copyout, case SUBSET: make sure that \t is balanced with \b.
 539:  * Actually it can only handle the case where a \t is in the subset and the
 540:  * matching \b is immediately following.
 541:  */
 542: 
 543: Hidden Procedure
 544: balance(ep)
 545:     environ *ep;
 546: {
 547:     string *rp = noderepr(tree(ep->focus));
 548:     int i;
 549:     int level = 0;
 550: 
 551:     Assert(ep->mode == SUBSET);
 552:     for (i = ep->s1/2; i*2 < ep->s2; ++i) {
 553:         if (rp[i]) {
 554:             if (index(rp[i], '\t'))
 555:                 ++level;
 556:             else if (index(rp[i], '\b'))
 557:                 --level;
 558:         }
 559:     }
 560:     if (level > 0 && i*2 == ep->s2 && rp[i] && index(rp[i], '\b'))
 561:         ep->s2 = 2*i + 1;
 562: }
 563: 
 564: 
 565: /*
 566:  * Copy the copy buffer to the focus.
 567:  */
 568: 
 569: Hidden bool
 570: copyin(ep, q)
 571:     register environ *ep;
 572:     /*auto*/ queue q;
 573: {
 574:     auto queue q2 = Qnil;
 575: 
 576:     if (!q) {
 577:         error(COPY_EMPTY); /* Empty copy buffer */
 578:         return No;
 579:     }
 580:     ep->changed = Yes;
 581:     q = qcopy(q);
 582:     if (!ins_queue(ep, &q, &q2)) {
 583:         qrelease(q2);
 584:         return No;
 585:     }
 586:     return app_queue(ep, &q2);
 587: }
 588: 
 589: 
 590: /*
 591:  * Find out whether the focus looks like a hole or if it has some real
 592:  * text in it.
 593:  * Assumes shrink(ep) has already been performed.
 594:  */
 595: 
 596: Visible bool
 597: ishole(ep)
 598:     register environ *ep;
 599: {
 600:     register int sym;
 601: 
 602:     switch (ep->mode) {
 603: 
 604:     case ATBEGIN:
 605:     case ATEND:
 606:     case VHOLE:
 607:     case FHOLE:
 608:         return Yes;
 609: 
 610:     case SUBLIST:
 611:     case SUBRANGE:
 612:         return No;
 613: 
 614:     case SUBSET:
 615:         return colonhack(ep); /* (Side-effect!) */
 616: 
 617:     case WHOLE:
 618:         sym = symbol(tree(ep->focus));
 619:         return sym == Optional || sym == Hole;
 620: 
 621:     default:
 622:         Abort();
 623:         /* NOTREACHED */
 624:     }
 625: }
 626: 
 627: 
 628: /*
 629:  * Amendment to ishole so that it categorizes '?: ?' as a hole.
 630:  * This makes deletion of empty refinements / alternative-suites
 631:  * easier (Steven).
 632:  */
 633: 
 634: Hidden bool
 635: colonhack(ep)
 636:     environ *ep;
 637: {
 638:     node n = tree(ep->focus);
 639:     node n1;
 640:     string *rp = noderepr(n);
 641:     int i;
 642:     int sym;
 643: 
 644:     for (i = ep->s1; i <= ep->s2; ++i) {
 645:         if (i&1) {
 646:             if (!allright(rp[i/2]))
 647:                 return No;
 648:         }
 649:         else {
 650:             n1 = child(n, i/2);
 651:             if (Type(n1) == Tex)
 652:                 return No;
 653:             sym = symbol(n1);
 654:             if (sym != Hole && sym != Optional)
 655:                 return No;
 656:         }
 657:     }
 658:     return Yes;
 659: }
 660: 
 661: 
 662: /*
 663:  * Refinement for colonhack.  Recognize strings that are almost blank
 664:  * (i.e. containing only spaces, colons and the allowed control characters).
 665:  */
 666: 
 667: Hidden bool
 668: allright(repr)
 669:     string repr;
 670: {
 671:     if (repr) {
 672:         for (; *repr; ++repr) {
 673:             if (!index(": \t\b\n\r", *repr))
 674:                 return No;
 675:         }
 676:     }
 677:     return Yes;
 678: }

Defined functions

allright defined in line 667; used 1 times
balance defined in line 543; used 2 times
colonhack defined in line 634; used 1 times
copyin defined in line 569; used 1 times
copyinout defined in line 420; used 1 times
copyout defined in line 445; used 5 times
delbody defined in line 62; used 2 times
delete defined in line 42; used 4 times
delfixed defined in line 129; used 1 times
  • in line 73
delhole defined in line 323; used 1 times
  • in line 49
delsublist defined in line 248; used 1 times
  • in line 80
delsubset defined in line 174; used 3 times
delvarying defined in line 96; used 1 times
  • in line 74
delwhole defined in line 304; used 1 times
  • in line 83
ishole defined in line 596; used 4 times
nonewline defined in line 506; used 2 times

Defined variables

rcsid defined in line 2; never used
Last modified: 1985-08-27
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 3980
Valid CSS Valid XHTML 1.0 Strict