1: /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */ 2: static char rcsid[] = "$Header: que1.c,v 2.4 84/10/26 12:04:28 guido Exp $"; 3: 4: /* 5: * B editor -- Manipulate queues of nodes, lower levels. 6: */ 7: 8: #include "b.h" 9: #include "feat.h" 10: #include "bobj.h" 11: #include "node.h" 12: #include "supr.h" 13: #include "queu.h" 14: #include "gram.h" 15: 16: #include <ctype.h> 17: 18: 19: value grab_com(); 20: 21: 22: /* 23: * Append queue 2 to the end of queue 1. 24: */ 25: 26: Visible Procedure 27: joinqueues(pq, q) 28: register queue *pq; 29: register queue q; 30: { 31: if (emptyqueue(q)) 32: return; 33: while (*pq) { 34: if (Refcnt(*pq) > 1) 35: uniql((value*)pq); 36: pq = &(*pq)->q_link; 37: } 38: *pq = q; 39: } 40: 41: 42: /* 43: * Prepend a node to a queue ("push"). 44: * Empty strings and Optional holes are silently discarded. 45: */ 46: 47: Visible Procedure 48: preptoqueue(n, pq) 49: node n; 50: register queue *pq; 51: { 52: register queue q; 53: 54: if (Type(n) == Tex) { 55: int len = Length((value)n); 56: if (len == 0) 57: return; 58: n = nodecopy(n); 59: } 60: else { /* Avoid Optional holes */ 61: if (symbol(n) == Optional) 62: return; 63: n = nodecopy(n); 64: } 65: q = (queue) grab_com(2); 66: q->q_data = n; 67: q->q_link = *pq; 68: *pq = q; 69: } 70: 71: 72: /* 73: * Append a node to the end of a queue (same extras as preptoqueue). 74: */ 75: 76: Visible Procedure 77: addtoqueue(pq, n) 78: register queue *pq; 79: register node n; 80: { 81: auto queue q = Qnil; 82: 83: preptoqueue(n, &q); 84: joinqueues(pq, q); 85: } 86: 87: 88: /* 89: * Push a string onto a queue. 90: */ 91: 92: Visible Procedure 93: stringtoqueue(str, pq) 94: register string str; 95: register queue *pq; 96: { 97: register value v; 98: 99: if (str == NULL) 100: return; 101: v = mk_text(str); 102: preptoqueue((node) v, pq); 103: release(v); 104: } 105: 106: 107: /* 108: * Append a string to a queue. 109: */ 110: 111: Visible Procedure 112: addstringtoqueue(pq, str) 113: register queue *pq; 114: register string str; 115: { 116: register value v = mk_text(str); 117: 118: addtoqueue(pq, (node) v); 119: release(v); 120: } 121: 122: 123: /* 124: * Get the first node of a queue and delink it ("pop"). 125: */ 126: 127: Visible node 128: queuebehead(pq) 129: register queue *pq; 130: { 131: register node n; 132: register queue q = *pq; 133: 134: Assert(q); 135: 136: n = nodecopy(q->q_data); 137: *pq = qcopy(q->q_link); 138: qrelease(q); 139: return n; 140: } 141: 142: 143: /* 144: * Split a node in successive queue elements which are pushed 145: * on the queue using preptoqueue. 146: * 'Atomic' nodes (texts and holes) are pushed unadorned. 147: */ 148: 149: Visible Procedure 150: splitnode(n, pq) 151: register node n; 152: register queue *pq; 153: { 154: register node nn; 155: register string *rp; 156: register int i; 157: register int sym; 158: 159: if (Type(n) == Tex) { 160: preptoqueue(n, pq); 161: return; 162: } 163: sym = symbol(n); 164: if (sym == Optional) 165: return; 166: if (sym == Hole) { 167: preptoqueue(n, pq); 168: return; 169: } 170: 171: rp = noderepr(n); 172: for (i = nchildren(n); i >= 0; --i) { 173: if (rp[i] && rp[i][0]) 174: stringtoqueue(rp[i], pq); 175: if (i) { 176: nn = child(n, i); 177: if (Type(nn) == Tex || symbol(nn) != Optional) 178: preptoqueue(nn, pq); 179: } 180: } 181: } 182: 183: 184: /* 185: * Substitute the focus for its parent, appending the remainder of 186: * the parent to the queue. 187: * The focus must be the first child and not preceded by fixed text. 188: * The focus must be allowed in the place of its parent. 189: * If any of these conditions is not met, No is returned and nothing 190: * is changed. 191: */ 192: 193: Visible bool 194: resttoqueue(pp, pq) 195: register path *pp; 196: register queue *pq; 197: { 198: auto queue q = Qnil; 199: register path pa = parent(*pp); 200: register node n = tree(*pp); 201: register int sym = symbol(n); 202: /* register markbits x; */ 203: 204: if (!pa || ichild(*pp) != 1 205: || fwidth(noderepr(tree(pa))[0]) != 0 || !allowed(pa, sym)) 206: return No; 207: 208: n = nodecopy(n); 209: /* x = marks(n); */ 210: up(pp) || Abort(); 211: splitnode(tree(*pp), &q); 212: noderelease(queuebehead(&q)); 213: replace(pp, n); 214: /* if (x) { */ 215: /* markpath(pp, x); */ /* Actually, should restore all n's marks? */ 216: /* } */ 217: joinqueues(pq, q); 218: return Yes; 219: } 220: 221: 222: /* 223: * Like resttoqueue, but exactly from current position in fixed text. 224: * Also, it cannot fail. 225: */ 226: 227: Visible Procedure 228: nosuggtoqueue(ep, pq) 229: register environ *ep; 230: queue *pq; 231: { 232: auto queue q = Qnil; 233: register int i; 234: register string *rp; 235: register node n; 236: register node nn; 237: register int sym; 238: string str; 239: 240: if (issuggestion(ep)) 241: return; 242: Assert((ep->mode == FHOLE || ep->mode == VHOLE) && (ep->s1&1)); 243: 244: n = tree(ep->focus); 245: rp = noderepr(n); 246: for (i = nchildren(n); i > ep->s1/2; --i) { 247: if (!Fw_zero(rp[i])) 248: stringtoqueue(rp[i], &q); 249: nn = child(n, i); 250: sym = symbol(nn); 251: if (sym != Optional) { 252: preptoqueue(nn, &q); 253: if (sym != Hole) { 254: s_downi(ep, i); 255: delfocus(&ep->focus); 256: s_up(ep); 257: } 258: } 259: } 260: str = rp[i]; 261: if (str && str[ep->s2]) /* Push partial first text */ 262: stringtoqueue(str + ep->s2, &q); 263: joinqueues(pq, q); 264: } 265: 266: 267: /* 268: * Check whether the remainder of the current node is all suggestion. 269: */ 270: 271: Visible bool 272: issuggestion(ep) 273: register environ *ep; 274: { 275: register node n; 276: register int nch; 277: register int sym; 278: register int i; 279: 280: if (ep->mode != VHOLE && ep->mode != FHOLE || !(ep->s1&1)) 281: return No; /* Actually wrong call? */ 282: 283: n = tree(ep->focus); 284: nch = nchildren(n); 285: for (i = ep->s1/2 + 1; i <= nch; ++i) { 286: sym = symbol(child(n, i)); 287: if (sym != Hole && sym != Optional) 288: return No; 289: } 290: return Yes; 291: } 292: 293: 294: /* 295: * See if a node fits in a hole. 296: */ 297: 298: Visible bool 299: fitnode(pp, n) 300: register path *pp; 301: register node n; 302: { 303: if (!allowed(*pp, symbol(n))) 304: return No; 305: replace(pp, nodecopy(n)); 306: return Yes; 307: } 308: 309: 310: /* 311: * Fit a string in a hole. 312: * Returns the number of characters consumed. 313: * (This does not have to be the maximum possible, but a reasonable attempt 314: * is made. If the internal buffer is exhausted, it leaves the rest for 315: * another call.) 316: */ 317: 318: Visible int 319: fitstring(pp, str, alt_c) 320: register path *pp; 321: register string str; 322: int alt_c; 323: { 324: environ dummyenv; 325: register node n; 326: register int ich; 327: register int len; 328: register string cp; 329: char buf[1024]; 330: 331: Assert(str); 332: if (!str[0]) 333: return 0; 334: if (!insguess(pp, str[0], &dummyenv)) { 335: if (!alt_c) 336: return 0; 337: if (!insguess(pp, alt_c, &dummyenv)) 338: return 0; 339: } 340: if (Type(tree(*pp)) == Tex) 341: up(pp) || Abort(); 342: if (dummyenv.mode == FHOLE) { 343: cp = noderepr(tree(*pp))[0]; 344: len = 1; 345: if (cp) { 346: ++str; 347: ++cp; 348: while (*str >= ' ' && *str == *cp) { 349: ++len; 350: ++str; 351: ++cp; 352: } 353: } 354: return len; 355: } 356: if (dummyenv.mode == VHOLE) { 357: buf[0] = str[0]; 358: ++str; 359: len = 1; 360: n = tree(*pp); 361: ich = dummyenv.s1/2; 362: while (*str && mayinsert(n, ich, len, *str) && len < sizeof buf - 1) { 363: buf[len] = *str; 364: ++str; 365: ++len; 366: } 367: if (len > 1) { 368: buf[len] = 0; 369: downi(pp, ich) || Abort(); 370: replace(pp, (node) mk_text(buf)); 371: up(pp) || Abort(); 372: } 373: return len; 374: } 375: return 1; 376: } 377: 378: 379: /* 380: * Set the focus position (some VHOLE/FHOLE setting, probably) 381: * at the 'len'th character from the beginning of the current node. 382: * This may involve going to a child or moving beyond the current subtree. 383: * Negative 'len' values may be given to indicate negative widths; 384: * this is implemented incomplete. 385: */ 386: 387: Visible Procedure 388: fixfocus(ep, len) 389: register environ *ep; 390: register int len; 391: { 392: node nn; 393: register node n = tree(ep->focus); 394: register string *rp; 395: register int i = 0; 396: register int nch; 397: register int w; 398: 399: if (Type(n) == Tex) { 400: w = Length((value)n); 401: Assert(w >= len && len >= 0); 402: if (w > len) 403: ep->spflag = No; 404: ep->mode = VHOLE; 405: ep->s1 = ichild(ep->focus) * 2; 406: ep->s2 = len; 407: s_up(ep); 408: return; 409: } 410: nch = nchildren(n); 411: w = width(n); 412: if (len > w && w >= 0) { 413: i = ichild(ep->focus); /* Change initial condition for for-loop */ 414: if (!up(&ep->focus)) { 415: ep->mode = ATEND; 416: return; 417: } 418: higher(ep); 419: n = tree(ep->focus); 420: } 421: 422: rp = noderepr(n); 423: for (; i <= nch; ++i) { 424: if (i) { 425: nn = child(n, i); 426: w = width(nn); 427: if (w < 0 || w >= len && len >= 0) { 428: s_downi(ep, i); 429: fixfocus(ep, len); 430: return; 431: } 432: if (len >= 0) 433: len -= w; 434: } 435: w = Fwidth(rp[i]); 436: if (w >= len && len >= 0) { 437: if (w > len) 438: ep->spflag = No; 439: ep->mode = FHOLE; 440: ep->s1 = 2*i + 1; 441: ep->s2 = len; 442: return; 443: } 444: else if (w < 0) 445: len = 0; 446: else 447: len -= w; 448: } 449: ep->mode = ATEND; 450: } 451: 452: 453: /* 454: * Apply, if possible, a special fix relating to spaces: 455: * when a space has been interpreted as joining character 456: * and we end up in the following hole, but we don't succeed 457: * in filling the hole; it is then tried to delete the hole 458: * and the space. 459: * Usually this doesn't occur, but it may occur when inserting 460: * after a space that was already fixed on the screen but now 461: * deserves re-interpretation. 462: */ 463: 464: Visible bool 465: spacefix(ep) 466: environ *ep; 467: { 468: path pa; 469: node n; 470: string *rp; 471: 472: if (ichild(ep->focus) != 2 || symbol(tree(ep->focus)) != Hole) 473: return No; 474: pa = parent(ep->focus); 475: n = tree(pa); 476: rp = noderepr(n); 477: if (!Fw_zero(rp[0]) || Fwidth(rp[1]) != 1 || rp[1][0] != ' ') 478: return No; 479: n = firstchild(n); 480: if (!allowed(pa, symbol(n))) 481: return No; 482: s_up(ep); 483: replace(&ep->focus, nodecopy(n)); 484: ep->mode = ATEND; 485: ep->spflag = Yes; 486: return Yes; 487: } 488: 489: 490: /* 491: * Prepend a subset of a node to a queue. 492: */ 493: 494: Visible Procedure 495: subsettoqueue(n, s1, s2, pq) 496: register node n; 497: register int s1; 498: register int s2; 499: register queue *pq; 500: { 501: register string *rp = noderepr(n); 502: 503: for (; s2 >= s1; --s2) { 504: if (s2&1) 505: stringtoqueue(rp[s2/2], pq); 506: else 507: preptoqueue(child(n, s2/2), pq); 508: } 509: } 510: 511: #ifdef SHOWBUF 512: 513: /* 514: * Produce flat text out of a queue's first line, to show it on screen. 515: */ 516: 517: Visible string 518: querepr(qv) 519: value qv; 520: { 521: queue q = (queue)qv; 522: node n; 523: static char buf[1000]; /***** Cannot overflow? *****/ 524: string cp; 525: string sp; 526: string *rp; 527: int nch; 528: int i; 529: int len; 530: 531: cp = buf; 532: for (; q; q = q->q_link) { 533: n = q->q_data; 534: if (Type(n) == Tex) { 535: for (sp = Str((value) n); cp < buf+80 && *sp; ++sp) { 536: if (!isprint(*sp) && *sp != ' ') 537: break; 538: *cp++ = *sp; 539: } 540: if (*sp == '\n') { 541: if (!emptyqueue(q->q_link)) { 542: strcpy(cp, " ..."); 543: cp += 4; 544: } 545: break; 546: } 547: } 548: else { 549: rp = noderepr(n); 550: nch = nchildren(n); 551: for (i = 0; i <= nch; ++i) { 552: if (i > 0) { 553: if (Type(child(n, i)) == Tex) { 554: len = Length((value)child(n, i)); 555: if (len > 80) 556: len = 80; 557: strncpy(cp, Str((value)child(n, i)), len); 558: cp += len; 559: } 560: else { 561: strcpy(cp, "..."); 562: cp += 3; 563: } 564: } 565: if (Fw_negative(rp[i])) { 566: strcpy(cp, " ..."); 567: cp += 4; 568: break; 569: } 570: if (Fw_positive(rp[i])) { 571: strcpy(cp, rp[i]); 572: while (*cp) 573: ++cp; 574: if (cp[-1] == '\t' || cp[-1] == '\b') 575: --cp; 576: } 577: } 578: } 579: if (cp >= buf+80) { 580: strcpy(buf+76, "..."); 581: break; 582: } 583: } 584: *cp = 0; 585: return buf; 586: } 587: 588: #endif SHOWBUF