home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / b / b.lha / B / src / bed / que2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-11-24  |  16.1 KB  |  853 lines

  1. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */
  2. static char rcsid[] = "$Header: que2.c,v 2.3 84/07/23 13:02:38 guido Exp $";
  3.  
  4. /*
  5.  * B editor -- Manipulate queues of nodes, higher levels.
  6.  */
  7.  
  8. #include <ctype.h>
  9.  
  10. #include "b.h"
  11. #include "feat.h"
  12. #include "bobj.h"
  13. #include "node.h"
  14. #include "supr.h"
  15. #include "queu.h"
  16. #include "gram.h"
  17. #include "tabl.h"
  18.  
  19.  
  20. extern bool lefttorite;
  21.     /* Set by edit() to signal we parse purely left-to-right */
  22. extern bool dflag; /* Debug mode even if NDEBUG on */
  23.  
  24.  
  25. /*
  26.  * Insert a queue of nodes at the focus
  27.  * (which had better be some kind of a hole).
  28.  * The nodes may also be a text, in which case the individual characters
  29.  * are inserted.
  30.  * Extensive changes to the parse tree may occur, and the node may be
  31.  * broken up in its constituent parts (texts and other nodes) which
  32.  * are then inserted individually.
  33.  */
  34.  
  35. Visible bool
  36. ins_queue(ep, pq, pq2)
  37.     register environ *ep;
  38.     register queue *pq;
  39.     register queue *pq2;
  40. {
  41.     register bool ok = Yes;
  42.     register node n;
  43.     register queue oldq2;
  44.     environ saveenv;
  45.     int oldindentation = focindent(ep);
  46.     int indentation = oldindentation;
  47.  
  48.     leftvhole(ep);
  49.     while (ok && !emptyqueue(*pq)) {
  50.         n = queuebehead(pq);
  51.         if (Type(n) == Tex) {
  52.             ok = ins_string(ep, Str((value) n), pq2, 0);
  53.             switch (Str((value) n)[Length((value) n) - 1]) { /* Last char */
  54.             case '\t':
  55.                 ++indentation;
  56.                 break;
  57.             case '\b':
  58.                 --indentation;
  59.                 break;
  60.             case '\n':
  61.                 while (focindent(ep) > indentation) {
  62.                     if (!ins_newline(ep))
  63.                         break;
  64.                 }
  65.                 break;
  66.             }
  67.         }
  68.         else {
  69.             Ecopy(*ep, saveenv);
  70.             oldq2 = qcopy(*pq2);
  71.             if (!ins_node(&saveenv, n, pq2)) {
  72.                 Erelease(saveenv);
  73.                 qrelease(*pq2);
  74.                 *pq2 = oldq2;
  75.                 if (symbol(n) == Hole)
  76.                     ok = ins_string(ep, "?", pq2, 0);
  77.                 else
  78.                     splitnode(n, pq);
  79.             }
  80.             else {
  81.                 Erelease(*ep);
  82.                 Emove(saveenv, *ep);
  83.                 qrelease(oldq2);
  84.             }
  85.         }
  86.         noderelease(n);
  87.     }
  88.     if (!ok)
  89.         qshow(*pq, "ins_queue");
  90.     qrelease(*pq);
  91.     for (indentation = focindent(ep);
  92.         indentation > oldindentation; --indentation)
  93.         stringtoqueue("\b", pq2); /* Pass on indentation to outer level */
  94.     return ok;
  95. }
  96.  
  97.  
  98. /*
  99.  * Subroutine to insert a queue to the right of the focus
  100.  * without affecting the focus position.
  101.  */
  102.  
  103. Visible bool
  104. app_queue(ep, pq)
  105.     environ *ep;
  106.     queue *pq;
  107. {
  108.     int where;
  109.     static int markbit = 1; /* To properly handle recursive calls */
  110.  
  111.     if (emptyqueue(*pq))
  112.         return Yes;
  113.     where = focoffset(ep);
  114.     markbit <<= 1;
  115.     markpath(&ep->focus, markbit);
  116.     if (!ins_queue(ep, pq, pq)) {
  117.         markbit >>= 1;
  118.         return No;
  119.     }
  120.     firstmarked(&ep->focus, markbit) || Abort();
  121.     unmkpath(&ep->focus, markbit);
  122.     markbit >>= 1;
  123.     ep->spflag = No;
  124.     fixfocus(ep, where);
  125.     return Yes;
  126. }
  127.  
  128.  
  129. /*
  130.  * Advance to next thing after current position.
  131.  */
  132.  
  133. Visible bool
  134. move_on(ep)
  135.     register environ *ep;
  136. {
  137.     register node n;
  138.     register string *rp;
  139.     register int sym;
  140.     register int ich = ichild(ep->focus);
  141.  
  142.     if (!up(&ep->focus))
  143.         return No;
  144.     higher(ep);
  145.     n = tree(ep->focus);
  146.     rp = noderepr(n);
  147.     if (Fw_positive(rp[ich])) {
  148.         ep->mode = FHOLE;
  149.         ep->s1 = 2*ich + 1;
  150.         ep->s2 = 0;
  151.         if (ep->spflag) {
  152.             ep->spflag = No;
  153.             if (rp[ich][0] == ' ') {
  154.                 ++ep->s2;
  155.                 if (fwidth(rp[ich]) > 1)
  156.                     return Yes;
  157.             }
  158.             else
  159.                 return Yes;
  160.         }
  161.         else
  162.             return Yes;
  163.     }
  164.     if (ich < nchildren(n)) {
  165.         s_downi(ep, ich+1);
  166.         sym = symbol(tree(ep->focus));
  167.         if (sym == Hole || sym == Optional)
  168.             ep->mode = WHOLE;
  169.         else
  170.             ep->mode = ATBEGIN;
  171.         return Yes;
  172.     }
  173.     ep->mode = ATEND;
  174.     return Yes;
  175. }
  176.  
  177.  
  178. /*
  179.  * Like move_on but moves through fixed texts, skipping only spaces
  180.  * and empty strings.
  181.  * <<<<< This code is a dinosaur and should be revised. >>>>>
  182.  */
  183.  
  184. Visible bool
  185. fix_move(ep)
  186.     register environ *ep;
  187. {
  188.     register int ich;
  189.     register int i;
  190.     register string *rp;
  191.     register string cp;
  192.  
  193.     Assert(ep->mode == FHOLE);
  194.  
  195.     ich = ep->s1/2;
  196.     rp = noderepr(tree(ep->focus));
  197.     cp = rp[ich];
  198.     if (cp) {
  199.         i = ep->s2;
  200.         Assert(i <= Fwidth(cp));
  201.         if (cp[i] == ' ') {
  202.             do {
  203.                 ++i;
  204.             } while (cp[i] == ' ');
  205.         }
  206.         if (cp[i] == '\b' || cp[i] == '\t') {
  207.             ++i;
  208.             Assert(!cp[i]);
  209.         }
  210.         else if (cp[i]) {
  211.             if (i == ep->s2)
  212.                 return No;
  213.             ep->s2 = i;
  214.             return Yes;
  215.         }
  216.     }
  217.  
  218.     if (ich >= nchildren(tree(ep->focus)))
  219.         ep->mode = ATEND;
  220.     else {
  221.         s_downi(ep, ich+1);
  222.         if (symbol(tree(ep->focus)) == Hole
  223.             || symbol(tree(ep->focus)) == Optional)
  224.             ep->mode = WHOLE;
  225.         else
  226.             ep->mode = ATBEGIN;
  227.     }
  228.     return Yes;
  229. }
  230.  
  231.  
  232. /*
  233.  * Insert a node in the parse tree.
  234.  */
  235.  
  236. Hidden bool
  237. ins_node(ep, n, pq)
  238.     register environ *ep;
  239.     register node n;
  240.     register queue *pq;
  241. {
  242.     register int sym;
  243.     register node nn;
  244.     register markbits x;
  245.     string *rp;
  246.  
  247.     if (symbol(n) == Optional)
  248.         return Yes;
  249.  
  250.     for (;;) {
  251.         switch (ep->mode) {
  252.  
  253.         case FHOLE:
  254.             if (ep->s2 < lenitem(ep) || !fix_move(ep))
  255.                 return No;
  256.             continue;
  257.  
  258.         case VHOLE:
  259.             if (ep->s2 < lenitem(ep) || !move_on(ep))
  260.                 return No;
  261.             continue;
  262.  
  263.         case ATBEGIN:
  264.             sym = symbol(tree(ep->focus));
  265.             if (sym == Optional || sym == Hole) {
  266.                 ep->mode = WHOLE;
  267.                 continue;
  268.             }
  269.             x = marks(tree(ep->focus));
  270.             if (joinnodes(&ep->focus, n, tree(ep->focus), No)) {
  271.                 if (x) {
  272.                     s_downi(ep, 2);
  273.                     markpath(&ep->focus, x);
  274.                     s_up(ep);
  275.                 }
  276.                 s_down(ep);
  277.                 ep->mode = ATEND;
  278.                 leftvhole(ep);
  279.                 return Yes;
  280.             }
  281.             nn = tree(ep->focus);
  282.             rp = noderepr(nn);
  283.             if (nchildren(nn) >= 1 && Fw_zero(rp[0])) {
  284.                 sym = symbol(firstchild(nn));
  285.                 if (sym == Hole || sym == Optional) {
  286.                     s_down(ep);
  287.                     if (fitnode(&ep->focus, n)) {
  288.                         ep->mode = ATEND;
  289.                         leftvhole(ep);
  290.                         return Yes;
  291.                     }
  292.                     s_up(ep);
  293.                 }
  294.             }
  295.             nn = nodecopy(nn);
  296.             if (!fitnode(&ep->focus, n)) {
  297.                 addtoqueue(pq, nn);
  298.                 noderelease(nn);
  299.                 delfocus(&ep->focus);
  300.                 ep->mode = WHOLE;
  301.                 continue;
  302.             }
  303.             if (downrite(&ep->focus)) {
  304.                 if (Type(tree(ep->focus)) != Tex) {
  305.                     sym = symbol(tree(ep->focus));
  306.                     if (sym == Hole || sym == Optional) {
  307.                         if (fitnode(&ep->focus, nn)) {
  308.                             noderelease(nn);
  309.                             nn = Nnil;
  310.                         }
  311.                     }
  312.                 }
  313.                 else
  314.                     up(&ep->focus);
  315.             }
  316.             if (nn) {
  317.                 addtoqueue(pq, nn);
  318.                 noderelease(nn);
  319.             }
  320.             ep->mode = ATEND;
  321.             leftvhole(ep);
  322.             return Yes;
  323.  
  324.         case WHOLE:
  325.             sym = symbol(tree(ep->focus));
  326.             Assert(sym == Optional || sym == Hole);
  327.             do {
  328.                 higher(ep); /* Only for second time around */
  329.                 if (fitnode(&ep->focus, n)) {
  330.                     ep->mode = ATEND;
  331.                     leftvhole(ep);
  332.                     return Yes;
  333.                 }
  334.             } while (resttoqueue(&ep->focus, pq));
  335.             ep->mode = ATEND;
  336.             /* Fall through */
  337.         case ATEND:
  338.             do {
  339.                 higher(ep); /* Only for second time around */
  340.                 if (joinnodes(&ep->focus, tree(ep->focus), n, ep->spflag)) {
  341.                     ep->spflag = No;
  342.                     leftvhole(ep);
  343.                     return Yes;
  344.                 }
  345.             } while (resttoqueue(&ep->focus, pq)
  346.                 || move_on(ep) && ep->mode == ATEND);
  347.             return No;
  348.  
  349.         default:
  350.             return No;
  351.  
  352.         }
  353.     }
  354. }
  355.  
  356.  
  357. /*
  358.  * Insert a string in the parse tree.
  359.  */
  360.  
  361. #define NEXT (++str, alt_c = 0)
  362.  
  363. Visible bool
  364. ins_string(ep, str, pq, alt_c)
  365.     register environ *ep;
  366.     /*auto*/ string str;
  367.     register queue *pq;
  368.     int alt_c;
  369. {
  370.     register node nn;
  371.     auto value v;
  372.     char buf[1024];
  373.     register string repr;
  374.     string oldstr;
  375.     register int sym;
  376.     register int len;
  377.     bool interactive = alt_c != 0;
  378.  
  379.     if (alt_c < 0)
  380.         alt_c = 0;
  381.     while (*str) {
  382.         switch (*str) {
  383.  
  384.         case '\n':
  385.             if (!ins_newline(ep))
  386.                 return No;
  387.             /* Fall through */
  388.         case '\t':
  389.         case '\b':
  390.             NEXT;
  391.             continue;
  392.  
  393.         }
  394.         switch (ep->mode) {
  395.  
  396.         case ATBEGIN:
  397.             nn = tree(ep->focus);
  398.             if (Type(nn) == Tex) {
  399.                 ep->s1 = 2*ichild(ep->focus);
  400.                 ep->s2 = 0;
  401.                 ep->mode = VHOLE;
  402.                 s_up(ep);
  403.                 continue;
  404.             }
  405.             sym = symbol(nn);
  406.             if (sym != Optional && sym != Hole) {
  407.                 if (fwidth(noderepr(nn)[0]) == 0) {
  408.                     if (down(&ep->focus))
  409.                         break;
  410.                 }
  411.                 addtoqueue(pq, nn);
  412.                 delfocus(&ep->focus);
  413.             }
  414.             ep->mode = WHOLE;
  415.             /* Fall through */
  416.         case WHOLE:
  417.             nn = tree(ep->focus);
  418.             sym = symbol(nn);
  419.             Assert(sym == Hole || sym == Optional);
  420.             while ((len = fitstring(&ep->focus, str, alt_c)) == 0) {
  421.                 if (sym == Optional) {
  422.                     if (!move_on(ep)) {
  423.                         if (*str == ' ')
  424.                             NEXT;
  425.                         else
  426.                             return No;
  427.                     }
  428.                     break;
  429.                 }
  430.                 if (!interactive && *str == '?') {
  431.                     NEXT;
  432.                     ep->mode = ATEND;
  433.                     break;
  434.                 }
  435.                 if (resttoqueue(&ep->focus, pq))
  436.                     higher(ep);
  437.                 else if (spacefix(ep))
  438.                     break;
  439.                 else if (*str == ' ') {
  440.                     NEXT;
  441.                     break;
  442.                 }
  443.                 else if (interactive)
  444.                     return No;
  445.                 else {
  446.                     ep->mode = ATEND;
  447.                     break;
  448.                 }
  449.             }
  450.             if (len > 0) {
  451.                 str += len;
  452.                 alt_c = 0;
  453.                 fixfocus(ep, len);
  454.             }
  455.             break;
  456.  
  457.         case ATEND:
  458.             if (add_string(ep, &str, alt_c)) {
  459.                 alt_c = 0;
  460.                 break;
  461.             }
  462.             len = joinstring(&ep->focus, str, ep->spflag,
  463.                 alt_c ? alt_c : interactive ? -1 : 0, Yes);
  464.             if (len > 0) {
  465.                 s_downi(ep, 2);
  466.                 ep->spflag = No;
  467.                 fixfocus(ep, len);
  468.             }
  469.             else {
  470.                 if (resttoqueue(&ep->focus, pq)) {
  471.                     higher(ep);
  472.                     break;
  473.                 }
  474.                 if (move_on(ep))
  475.                     break;
  476.                 if (*str == ' ') {
  477.                     NEXT;
  478.                     break;
  479.                 }
  480.                 return No;
  481.             }
  482.             str += len;
  483.             alt_c = 0;
  484.             break;
  485.  
  486.         case FHOLE:
  487.             nn = tree(ep->focus);
  488.             repr = noderepr(nn)[ep->s1/2];
  489.             if (ep->s2 >= fwidth(repr)
  490.                 && (ep->s2 <= 0 || ep->spflag || !isalpha(repr[0])
  491.                     || repr[ep->s2-1] == ' ')) { /* At end */
  492.                 if (ep->s1/2 < nchildren(nn)) {
  493.                     s_downi(ep, ep->s1/2 + 1);
  494.                     ep->mode = ATBEGIN; /* Of next child */
  495.                 }
  496.                 else
  497.                     ep->mode = ATEND;
  498.                 break;
  499.             }
  500.             if ((*str == ':' || *str == ' ') && *str == repr[ep->s2]) {
  501.                 /*****
  502.                  * Quick hack for insertion of test-suites and refinements:
  503.                  *****/
  504.                 ++ep->s2;
  505.                 NEXT;
  506.                 continue;
  507.             }
  508.             if (!lefttorite)
  509.                 nosuggtoqueue(ep, pq);
  510.             oldstr = str;
  511.             if (resuggest(ep, &str, alt_c) || soften(ep, &str, alt_c)) {
  512.                 if (str > oldstr)
  513.                     alt_c = 0;
  514.                 continue;
  515.             }
  516.             if (fix_move(ep))
  517.                 continue;
  518.             return No;
  519.  
  520.         case VHOLE:
  521.             Assert(!(ep->s1&1));
  522.             nn = tree(ep->focus);
  523. #ifdef USERSUGG
  524.             if (symbol(nn) == Suggestion) {
  525.                 if (newsugg(ep, &str, alt_c))
  526.                     alt_c = 0;
  527.                 else
  528.                     killsugg(ep);
  529.                 continue;
  530.             }
  531. #endif USERSUGG
  532.             s_downi(ep, ep->s1/2);
  533.             v = copy((value) tree(ep->focus));
  534.             len = 0;
  535.             if (!ep->spflag) {
  536.                 for (; len < sizeof buf - 1 && str[len]
  537.                         && mayinsert(nn, ep->s1/2, !!(ep->s2 + len),
  538.                             str[len]);
  539.                     ++len) {
  540.                     buf[len] = str[len];
  541.                 }
  542.                 if (len <= 0 && alt_c
  543.                     && mayinsert(nn, ep->s1/2, !!(ep->s2 + len), alt_c)) {
  544.                     buf[0] = alt_c;
  545.                     len = 1;
  546.                 }
  547.             }
  548.             if (len > 0) { /* Effectuate change */
  549.                 str += len;
  550.                 alt_c = 0;
  551.                 Assert(Type(v) == Tex);
  552.                 buf[len] = 0;
  553.                 putintrim(&v, ep->s2, Length(v) - ep->s2, buf);
  554.                 replace(&ep->focus, (node) v);
  555.                 s_up(ep);
  556.                 ep->spflag = No;
  557.                 ep->s2 += len;
  558.             }
  559.             else { /* Nothing inserted */
  560.                 if (ep->s2 == 0) { /* Whole string rejected */
  561.                     addtoqueue(pq, (node)v);
  562.                     release(v);
  563.                     s_up(ep);
  564.                     delfocus(&ep->focus);
  565.                     ep->mode = WHOLE;
  566.                     break;
  567.                 }
  568.                 if (ep->s2 < Length(v)) {
  569.                     addstringtoqueue(pq, Str(v) + ep->s2);
  570.                     putintrim(&v, ep->s2, 0, "");
  571.                     replace(&ep->focus, (node) v);
  572.                 }
  573.                 else
  574.                     release(v);
  575.                 move_on(ep) || Abort(); /* ==> up, cancelling s_downi! */
  576.             }
  577.             break;
  578.  
  579.         default:
  580.             Abort();
  581.  
  582.         }
  583.     }
  584.  
  585.     return Yes;
  586. }
  587.  
  588.  
  589. /*
  590.  * See if two nodes can be joined in a hole.
  591.  * 'Spflag' indicates whether a space must be present between the nodes
  592.  * (required or forbidden).
  593.  * Either of n1, n2 may actually be the current contents of the hole.
  594.  */
  595.  
  596. Hidden bool
  597. joinnodes(pp, n1, n2, spflag)
  598.     path *pp;
  599.     node n1;
  600.     node n2;
  601.     bool spflag;
  602. {
  603.     path pa = parent(*pp);
  604.     int sympa = pa ? symbol(tree(pa)) : Rootsymbol;
  605.     struct table *tp = &table[sympa];
  606.     struct classinfo *ci = tp->r_class[ichild(*pp) - 1];
  607.     classptr cp = ci->c_join;
  608.     int sym1 = symbol(n1);
  609.     int sym2 = symbol(n2);
  610.     int symcp;
  611.     int symfound = -1;
  612.  
  613.     if (!cp)
  614.         return No;
  615.     for (; *cp; cp += 2) {
  616.         if (cp[0] != spflag + 1)
  617.             continue;
  618.         symcp = cp[1];
  619.         tp = &table[symcp];
  620.         if (isinclass(sym1, tp->r_class[0])
  621.             && isinclass(sym2, tp->r_class[1])) {
  622.             symfound = symcp;
  623.             break;
  624.         }
  625.     }
  626.  
  627.     if (symfound < 0)
  628.         return No;
  629.     n1 = nodecopy(n1);
  630.     n2 = nodecopy(n2); /* 'Cause one of them may overlap tree(*pp) */
  631.     replace(pp, table[symfound].r_node);
  632.     down(pp) || Abort();
  633.     replace(pp, n1);
  634.     rite(pp) || Abort();
  635.     replace(pp, n2);
  636.     up(pp) || Abort();
  637.     return Yes;
  638. }
  639.  
  640.  
  641. /*
  642.  * Try to join a node (implicit as tree(*pp)) with some text.
  643.  * That is, try to replace the node by one with it as first child,
  644.  * (some of) the text as second child, and nothing or a space in between.
  645.  *
  646.  * 'Spflag' indicates whether a space is desirable between the nodes
  647.  * (but if No it is only used as advice).
  648.  *
  649.  * Returns the number of characters consumed from str.
  650.  */
  651.  
  652. Visible int
  653. joinstring(pp, str, spflag, alt_c, mayindent)
  654.     path *pp;
  655.     register string str;
  656.     register bool spflag;
  657.     int alt_c;
  658.     bool mayindent;
  659. {
  660.     register struct table *tp;
  661.     path pa = parent(*pp);
  662.     node n1;
  663.     struct classinfo *ci;
  664.     register classptr cp;
  665.     int sympa = pa ? symbol(tree(pa)) : Rootsymbol;
  666.     register int sym1;
  667.     register int symcp;
  668.     int symfound;
  669.     int len;
  670.     char buf[2];
  671.     bool interactive = alt_c != 0;
  672.  
  673.     if (alt_c < 0)
  674.         alt_c = 0;
  675.     ci = table[sympa].r_class[ichild(*pp) - 1];
  676.     Assert(ci);
  677.     cp = ci->c_join;
  678.     if (!cp)
  679.         return 0;
  680.  
  681.     n1 = tree(*pp);
  682.     sym1 = symbol(n1);
  683.     symfound = -1;
  684.     for (; *cp; cp += 2) {
  685.         if (cp[0] < spflag + 1)
  686.             continue;
  687.         symcp = cp[1];
  688.         tp = &table[symcp];
  689.         if (!mayindent && tp->r_repr[1] && index(tp->r_repr[1], '\t'))
  690.             continue;
  691.         if (isinclass(sym1, tp->r_class[0])
  692.             && ((canfitchar(str[0], tp->r_class[1]))
  693.                 || str[0] == '?' && !interactive)) {
  694.             if (cp[0] == spflag + 1) {
  695.                 symfound = symcp;
  696.                 break;
  697.             }
  698.             if (symfound < 0)
  699.                 symfound = symcp;
  700.         }
  701.     }
  702.  
  703.     if (symfound < 0) { /* 1-level recursion */
  704.         if (!alt_c)
  705.             return 0;
  706.         buf[0] = alt_c;
  707.         buf[1] = 0;
  708.         return joinstring(pp, buf, spflag, 0, mayindent);
  709.     }
  710.     n1 = nodecopy(n1); /* 'Cause it overlaps tree(*pp) */
  711.     replace(pp, table[symfound].r_node);
  712.     down(pp) || Abort();
  713.     replace(pp, n1);
  714.     rite(pp) || Abort();
  715.     len = fitstring(pp, str, 0);
  716.     if (len == 0 && str[0] == '?')
  717.         len = 1;
  718.     Assert(len > 0); /* Disagreement between canfitchar and fitstring */
  719.     up(pp) || Abort();
  720.     return len;
  721. }
  722.  
  723.  
  724. /*
  725.  * Similar to joinstring, but now the string must match the delimiter
  726.  * rather than being acceptable as second child.
  727.  * (Interface has changed to resemble resuggest/soften.)
  728.  */
  729.  
  730. Hidden bool
  731. add_string(ep, pstr, alt_c)
  732.     environ *ep;
  733.     string *pstr;
  734.     int alt_c; /* Yet unused */
  735. {
  736.     register struct table *tp;
  737.     path pa = parent(ep->focus);
  738.     node n1;
  739.     struct classinfo *ci;
  740.     register classptr cp;
  741.     int sympa = pa ? symbol(tree(pa)) : Rootsymbol;
  742.     register int sym1;
  743.     register int symcp;
  744.     register int c;
  745.  
  746.     ci = table[sympa].r_class[ichild(ep->focus) - 1];
  747.     Assert(ci);
  748.     cp = ci->c_append;
  749.     if (!cp)
  750.         return No;
  751.     n1 = tree(ep->focus);
  752.     sym1 = symbol(n1);
  753.     c = **pstr;
  754.     for (; *cp; cp += 2) {
  755.         if ((*cp&0177) != c)
  756.             continue;
  757.         symcp = cp[1];
  758.         tp = &table[symcp];
  759.         if (isinclass(sym1, tp->r_class[0]))
  760.             break;
  761.     }
  762.     if (!*cp)
  763.         return No;
  764.     ++*pstr;
  765.     if (c == ' ') {
  766.         ep->spflag = Yes;
  767.         return Yes;
  768.     }
  769.     n1 = nodecopy(n1); /* 'Cause it overlaps tree(ep->focus) */
  770.     replace(&ep->focus, table[symcp].r_node);
  771.     s_down(ep);
  772.     replace(&ep->focus, n1);
  773.     s_up(ep);
  774.     ep->mode = FHOLE;
  775.     ep->s1 = 3;
  776.     ep->s2 = (*cp&0200) ? 2 : 1;
  777.     ep->spflag = No;
  778.     return Yes;
  779. }
  780.  
  781.  
  782. /*
  783.  * See whether a character may start a new node in a hole with given class.
  784.  */
  785.  
  786. Visible bool
  787. canfitchar(c, ci)
  788.     int c;
  789.     struct classinfo *ci;
  790. {
  791.     register classptr cp;
  792.     register int code = Code(c);
  793.  
  794.     Assert(ci);
  795.     cp = ci->c_insert;
  796.     Assert(cp);
  797.     for (; *cp; cp += 2) {
  798.         if (cp[0] == code)
  799.             return Yes;
  800.     }
  801.     return No;
  802. }
  803.  
  804.  
  805. /*
  806.  * Debug routine to print a queue.
  807.  */
  808.  
  809. Visible Procedure
  810. qshow(q, where)
  811.     queue q;
  812.     string where;
  813. {
  814. #ifndef NDEBUG
  815.     node n;
  816.     char buf[256];
  817.     string cp;
  818.     string sp;
  819.  
  820.     sprintf(buf, "%s:", where);
  821.     cp = buf + strlen(buf);
  822.     for (;q; q = q->q_link) {
  823.         n = q->q_data;
  824.         *cp++ = ' ';
  825.         if (Type(n) == Tex) {
  826.             *cp++ = '"';
  827.             for (sp = Str((value) n); *sp; ++sp) {
  828.                 if (isprint(*sp) || *sp == ' ') {
  829.                     *cp++ = *sp;
  830.                     if (*sp == '"')
  831.                         *cp++ = *sp;
  832.                 }
  833.                 else {
  834.                     sprintf(cp, "\\%03o", *sp&0377);
  835.                     cp += 4;
  836.                 }
  837.             }
  838.             *cp++ = '"';
  839.         }
  840.         else {
  841.             strncpy(cp, table[symbol(n)].r_name, 80);
  842.             cp += strlen(cp);
  843.         }
  844.         if (cp >= buf+80) {
  845.             strcpy(buf+76, "...");
  846.             break;
  847.         }
  848.     }
  849.     *cp = 0;
  850.     debug(buf);
  851. #endif NDEBUG
  852. }
  853.