home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / APPS / lout2.lzh / LOUT2 / z13.c < prev    next >
Text File  |  1994-01-23  |  20KB  |  527 lines

  1. /*@z13.c:Object Breaking:BreakJoinedGroup()@**********************************/
  2. /*                                                                           */
  3. /*  LOUT: A HIGH-LEVEL LANGUAGE FOR DOCUMENT FORMATTING (VERSION 2.05)       */
  4. /*  COPYRIGHT (C) 1993 Jeffrey H. Kingston                                   */
  5. /*                                                                           */
  6. /*  Jeffrey H. Kingston (jeff@cs.su.oz.au)                                   */
  7. /*  Basser Department of Computer Science                                    */
  8. /*  The University of Sydney 2006                                            */
  9. /*  AUSTRALIA                                                                */
  10. /*                                                                           */
  11. /*  This program is free software; you can redistribute it and/or modify     */
  12. /*  it under the terms of the GNU General Public License as published by     */
  13. /*  the Free Software Foundation; either version 1, or (at your option)      */
  14. /*  any later version.                                                       */
  15. /*                                                                           */
  16. /*  This program is distributed in the hope that it will be useful,          */
  17. /*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
  18. /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
  19. /*  GNU General Public License for more details.                             */
  20. /*                                                                           */
  21. /*  You should have received a copy of the GNU General Public License        */
  22. /*  along with this program; if not, write to the Free Software              */
  23. /*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                */
  24. /*                                                                           */
  25. /*  FILE:         z13.c                                                      */
  26. /*  MODULE:       Object Breaking                                            */
  27. /*  EXTERNS:      BreakObject()                                              */
  28. /*                                                                           */
  29. /*****************************************************************************/
  30. #include "externs"
  31.  
  32.  
  33. /*****************************************************************************/
  34. /*                                                                           */
  35. /*  static BreakJoinedGroup(start, stop, m, c, res_back, res_fwd)            */
  36. /*                                                                           */
  37. /*  Break joined group of components of a VCAT, beginning from Child(start)  */
  38. /*  inclusive and ending at Child(stop) inclusive.  Break component m first  */
  39. /*  because it is the widest.                                                */
  40. /*                                                                           */
  41. /*****************************************************************************/
  42.  
  43. static BreakJoinedGroup(start, stop, m, c, res_back, res_fwd)
  44. OBJECT start, stop, m;  CONSTRAINT *c;  LENGTH *res_back, *res_fwd;
  45. { OBJECT y, link, z;  LENGTH b, f;  CONSTRAINT yc;
  46.   ifdebug(DOB, D, Child(y, start));
  47.   ifdebug(DOB, D, Child(z, stop));
  48.   debug3(DOB, D, "BreakJoinedGroup( -> %s, -> %s, %s, -, -)",
  49.     EchoObject(y), EchoObject(z), EchoConstraint(c));
  50.   CopyConstraint(yc, *c);
  51.   if( m != nil )
  52.   { m = BreakObject(m, &yc);
  53.     b = back(m, COL);
  54.     f = fwd(m, COL);
  55.     SetConstraint(yc, min(bc(yc), bfc(yc)-f), bfc(yc), min(fc(yc), bfc(yc)-b));
  56.   }
  57.   else b = f = 0;
  58.   for( link = start;  link != NextDown(stop);  link = NextDown(link) )
  59.   { Child(y, link);
  60.     if( !is_definite(type(y)) || y == m )  continue;
  61.     y = BreakObject(y, &yc);
  62.     b = max(b, back(y, COL));
  63.     f = max(f, fwd(y, COL));
  64.     SetConstraint(yc, min(bc(yc), bfc(yc)-f), bfc(yc), min(fc(yc), bfc(yc)-b));
  65.   }
  66.   assert( FitsConstraint(b, f, *c), "BreakJoinedGroup: result does not fit!" );
  67.   *res_back = b;  *res_fwd = f;
  68.   debug2(DOB,D,"BreakJoinedGroup returning (%s, %s)",
  69.     EchoLength(b), EchoLength(f));
  70. } /* end BreakJoinedGroup */
  71.  
  72.  
  73. /*@::BreakVcat()@*************************************************************/
  74. /*                                                                           */
  75. /*  static OBJECT BreakVcat(x, c)                                            */
  76. /*                                                                           */
  77. /*  Break a VCAT to satisfy constraint c.  This is tedious because every     */
  78. /*  group of components between //  ...  // must be broken separately.       */
  79. /*                                                                           */
  80. /*****************************************************************************/
  81.  
  82. OBJECT BreakVcat(x, c)
  83. OBJECT x;  CONSTRAINT *c;
  84. { OBJECT y, link, start_group, m;  LENGTH b, f; int dble_fwd;  CONSTRAINT tc;
  85.   BOOLEAN dble_found;
  86.   debug2(DOB, D, "[ BreakVcat(%s, %s)", EchoObject(x), EchoConstraint(c));
  87.   assert(Down(x) != x, "BreakVcat: Down(x) == x!" );
  88.   SetConstraint(tc, MAX_LEN, min(bfc(*c), fc(*c)), MAX_LEN);
  89.   
  90.   dble_found = FALSE;  dble_fwd = 0;  start_group = nil;
  91.   for( link = Down(x);  link != x;  link = NextDown(link) )
  92.   { Child(y, link);
  93.     if( is_index(type(y)) )  continue;
  94.     if( type(y) == GAP_OBJ )
  95.     { assert( start_group != nil, "BreakVcat: start_group == nil!" );
  96.       if( !join(gap(y)) )
  97.       {
  98.     /* finish off and break this group */
  99.     if( !FitsConstraint(b, f, tc) )
  100.       BreakJoinedGroup(start_group, link, m, &tc, &b, &f);
  101.     dble_found = TRUE;
  102.     dble_fwd = max(dble_fwd, b + f);
  103.     start_group = nil;
  104.     debug1(DOB, D, "  end group, dble_fwd: %s", EchoLength(dble_fwd));
  105.       }
  106.     }
  107.     else if( start_group == nil )
  108.     {    
  109.       /* start new group */
  110.       b = back(y, COL);  f = fwd(y, COL);
  111.       start_group = link;  m = y;
  112.       debug3(DOB, D, "  starting group: %s (%s, %s)",
  113.     EchoObject(y), EchoLength(b), EchoLength(f));
  114.     }
  115.     else
  116.     {
  117.       /* continue with current group */
  118.       b = max(b, back(y, COL));  f = max(f, fwd(y, COL));
  119.       if( fwd(y, COL) > fwd(m, COL) )  m = y;
  120.       debug4(DOB, D, "  in group: %s%s (%s, %s)", m == y ? "new max " : "",
  121.     EchoObject(y), EchoLength(b), EchoLength(f));
  122.     }
  123.   }
  124.   assert( start_group != nil, "BreakVcat: start_group == nil (2)!" );
  125.  
  126.   if( dble_found )
  127.   {    
  128.     /* finish off and break this last group, and set sizes of x */
  129.     if( !FitsConstraint(b, f, tc) )
  130.       BreakJoinedGroup(start_group, LastDown(x), m, &tc, &b, &f);
  131.     dble_fwd = max(dble_fwd, b + f);
  132.     debug1(DOB, D, "  ending last group, dble_fwd: %s",EchoLength(dble_fwd));
  133.     back(x, COL) = 0;  fwd(x, COL) = min(MAX_LEN, dble_fwd);
  134.   }
  135.   else
  136.   {
  137.     /* finish off and break this last and only group, and set sizes of x */
  138.     debug2(DOB, D, "  BreakVcat ending last and only group (%s, %s)",
  139.     EchoLength(b), EchoLength(f));
  140.     BreakJoinedGroup(start_group, LastDown(x), m, c, &b, &f);
  141.     back(x, COL) = b;  fwd(x, COL) = f;
  142.   }
  143.  
  144.   debug1(DOB, D, "] BreakVcat returning %s", EchoObject(x));
  145.   debug2(DOB, D, "  (size is %s, %s)",
  146.     EchoLength(back(x, COL)), EchoLength(fwd(x, COL)));
  147.   return x;
  148. } /* end BreakVcat */
  149.  
  150.  
  151. /*@::BreakTable()@************************************************************/
  152. /*                                                                           */
  153. /*  static OBJECT BreakTable(x, c)                                           */
  154. /*                                                                           */
  155. /*  Break table (HCAT) x to satisfy constraint c.                            */
  156. /*                                                                           */
  157. /*  Outline of algorithm:                                                    */
  158. /*                                                                           */
  159. /*     bcount = number of components to left of mark;                        */
  160. /*     fcount = no. of components on and right of mark;                      */
  161. /*     bwidth = what back(x) would be if all components had size (0, 0);     */
  162. /*     fwidth = what fwd(x) would be if all components had size (0, 0);      */
  163. /*     Set all components of x to Unbroken (broken(y) holds this flag);      */
  164. /*     while( an Unbroken component of x exists )                            */
  165. /*     {   my = the Unbroken component of x of minimum width;                */
  166. /*         mc = desirable constraint for my (see below);                     */
  167. /*         BreakObject(my, &mc);                                             */
  168. /*         Set my to Broken and update bcount, fcount, bwidth, fwidth        */
  169. /*            to reflect the actual size of my, now broken;                  */
  170. /*     }                                                                     */
  171. /*                                                                           */
  172. /*  The constraint mc is chosen in an attempt to ensure that:                */
  173. /*                                                                           */
  174. /*     a)  Any sufficiently narrow components will not break;                */
  175. /*     b)  All broken components will have the same bfc(mc), if possible;    */
  176. /*     c)  All available space is used.                                      */
  177. /*                                                                           */
  178. /*****************************************************************************/
  179.  
  180. static OBJECT BreakTable(x, c)
  181. OBJECT x;  CONSTRAINT *c;
  182. { LENGTH bwidth, fwidth;    /* running back(x) and fwd(x)             */
  183.   int    bcount, fcount;    /* running no. of components             */
  184.   OBJECT mlink, my;        /* minimum-width unbroken component         */
  185.   BOOLEAN ratm;            /* TRUE when my has a mark to its right      */
  186.   int    mside;            /* side of the mark my is on: BACK, ON, FWD  */
  187.   LENGTH msize;            /* size of my (minimal among unbroken)         */
  188.   CONSTRAINT mc;        /* desirable constraint for my             */
  189.   OBJECT pg, prec_def;        /* preceding definite object of my           */
  190.   OBJECT sg, succ_def;        /* succeeding definite object of my          */
  191.   LENGTH pd_extra, sd_extra;    /* space availiable for free each side of my */
  192.   LENGTH av_colsize;        /* the size of each unbroken component       */
  193.                 /* if they are all assigned equal width      */
  194.   LENGTH fwd_max, back_max;    /* maximum space available forward of or     */
  195.                 /* back of the mark, when columns are even   */
  196.   LENGTH col_size;        /* the column size actually used in breaking */
  197.   LENGTH beffect, feffect;    /* the amount bwidth, fwidth must increase   */
  198.                 /* when my is broken                 */
  199.   OBJECT link, y, prev, g;  LENGTH tmp, tmp2;
  200.  
  201.   debug2(DOB, DD, "[ BreakTable( %s, %s )", EchoObject(x), EchoConstraint(c));
  202.  
  203.   /* Initialise csize, bcount, fcount, bwidth, fwidth and broken(y) */
  204.   bcount = fcount = 0;  bwidth = fwidth = 0;  prev = nil;
  205.   Child(y, Down(x));
  206.   assert( type(y) != GAP_OBJ, "BreakTable: GAP_OBJ!" );
  207.   assert( !is_index(type(y)), "BreakTable: index!" );
  208.   broken(y) = is_indefinite(type(y));
  209.   if( !broken(y) )  prev = y, fcount = 1;
  210.  
  211.   for( link = NextDown(Down(x));  link != x;  link = NextDown(NextDown(link)) )
  212.   {
  213.     /* find the next gap g and following child y */
  214.     Child(g, link);
  215.     assert( type(g) == GAP_OBJ, "BreakTable: GAP_OBJ!" );
  216.     assert( NextDown(link) != x, "BreakTable: GAP_OBJ is last!" );
  217.     Child(y, NextDown(link));
  218.  
  219.     assert( type(y) != GAP_OBJ, "BreakTable: GAP_OBJ!" );
  220.     assert( !is_index(type(y)), "BreakTable: index!" );
  221.     broken(y) = is_indefinite(type(y));
  222.     if( !broken(y) )
  223.     { if( prev == nil )  fcount = 1;
  224.       else if( mark(gap(g)) )
  225.       {    bcount += fcount;
  226.     bwidth += fwidth + MinGap(0, 0, 0, &gap(g));
  227.     fcount  = 1;  fwidth = 0;
  228.       }
  229.       else
  230.       {    fwidth += MinGap(0, 0, 0, &gap(g));
  231.     fcount += 1;
  232.       }
  233.       prev = y;
  234.     }
  235.   }
  236.  
  237.   /* if column gaps alone are too wide, kill them all */
  238.   if( !FitsConstraint(bwidth, fwidth, *c) )
  239.   { Error(WARN, &fpos(x), "object too wide: reducing column gaps to 0i");
  240.     for( link = Down(x);  link != x;  link = NextDown(link) )
  241.     { Child(g, link);
  242.       if( type(g) == GAP_OBJ )
  243.       {    SetGap(gap(g), mark(gap(g)), join(gap(g)), FIXED_UNIT, EDGE_MODE, 0);
  244.       }
  245.     }
  246.     bwidth = fwidth = 0;
  247.   }
  248.  
  249.   /* break each column, from smallest to largest */
  250.   while( bcount + fcount > 0 && FitsConstraint(bwidth, fwidth, *c) )
  251.   {
  252.     debug2(DOB,DD, "bcount: %d;  bwidth: %s", bcount, EchoLength(bwidth));
  253.     debug2(DOB,DD, "fcount: %d;  fwidth: %s", fcount, EchoLength(fwidth));
  254.  
  255.     /* find a minimal-width unbroken component my */
  256.     my = nil;  msize = size(x, COL);       /* an upper bound for size(y) */
  257.     for( link = Down(x);  ;  link = NextDown(link) )
  258.     { Child(y, link);
  259.       assert( type(y) != GAP_OBJ, "BreakTable: type(y) == GAP_OBJ!" );
  260.       if( !broken(y) && (size(y, COL) < msize || my == nil) )
  261.       {    msize = size(y, COL);
  262.     my = y;  mlink = link;
  263.     ratm = FALSE;
  264.       }
  265.  
  266.       /* next gap */
  267.       link = NextDown(link);
  268.       if( link == x )  break;
  269.       Child(g, link);
  270.       assert( type(g) == GAP_OBJ, "BreakTable: type(g) != GAP_OBJ!" );
  271.       if( mark(gap(g)) )  ratm = TRUE;
  272.     }
  273.  
  274.     /* find neighbouring definite objects and resulting pd_extra and sd_extra */
  275.     SetNeighbours(mlink, ratm, &pg, &prec_def, &sg, &succ_def, &mside);
  276.     debug2(DOB, DD, "my (%s): %s", Image(mside), EchoObject(my));
  277.     pd_extra = pg == nil ? 0 :
  278.       ExtraGap(broken(prec_def) ? fwd(prec_def,COL) : 0, 0, &gap(pg), BACK);
  279.     sd_extra = sg == nil ? 0 :
  280.       ExtraGap(0, broken(succ_def) ? back(succ_def,COL) : 0, &gap(sg), FWD);
  281.     debug2(DOB, DD, "pd_extra:   %s;  sd_extra:      %s",
  282.         EchoLength(pd_extra), EchoLength(sd_extra) );
  283.  
  284.     /* calculate desirable constraints for my */
  285.     av_colsize = (bfc(*c) - bwidth - fwidth) / (bcount + fcount);
  286.     debug1(DOB, DD, "av_colsize = %s", EchoLength(av_colsize));
  287.     switch( mside )
  288.     {
  289.  
  290.       case BACK:
  291.       
  292.     back_max = min(bc(*c), bwidth + av_colsize * bcount);
  293.     col_size = (back_max - bwidth) / bcount;
  294.     SetConstraint(mc, col_size + pd_extra, col_size + pd_extra + sd_extra,
  295.             col_size + sd_extra );
  296.     break;
  297.  
  298.  
  299.       case ON:
  300.       
  301.     fwd_max = min(fc(*c), fwidth + av_colsize * fcount);
  302.     col_size = (fwd_max - fwidth) / fcount;
  303.     SetConstraint(mc, pd_extra + back(my, COL), MAX_LEN, col_size + sd_extra);
  304.     break;
  305.  
  306.  
  307.       case FWD:
  308.       
  309.     fwd_max = min(fc(*c), fwidth + av_colsize * fcount);
  310.     col_size = (fwd_max - fwidth) / fcount;
  311.     SetConstraint(mc, col_size + pd_extra, col_size + pd_extra + sd_extra,
  312.             col_size + sd_extra );
  313.     break;
  314.  
  315.  
  316.       default:
  317.       
  318.     Error(INTERN, no_fpos, "BreakTable found illegal side");
  319.     break;
  320.     }
  321.  
  322.     /* now break my according to these constraints, and accept it */
  323.     my = BreakObject(my, &mc);  broken(my) = TRUE;
  324.  
  325.     /* calculate the effect of accepting my on bwidth and fwidth */
  326.     if( pg != nil )
  327.     { tmp = broken(prec_def) ? fwd(prec_def, COL) : 0;
  328.       beffect = MinGap(tmp, back(my, COL), fwd(my, COL), &gap(pg)) -
  329.             MinGap(tmp, 0,             0,            &gap(pg));
  330.     }
  331.     else beffect = back(my, COL);
  332.  
  333.     if( sg != nil )
  334.     { tmp = broken(succ_def) ? back(succ_def, COL) : 0;
  335.       tmp2 = broken(succ_def) ? fwd(succ_def, COL) : 0;
  336.       feffect = MinGap(fwd(my, COL), tmp, tmp2, &gap(sg)) -
  337.             MinGap(0,            tmp, tmp2, &gap(sg));
  338.     }
  339.     else feffect = fwd(my, COL);
  340.  
  341.     switch( mside )
  342.     {
  343.     case BACK:    bwidth += beffect + feffect;
  344.             bcount--;
  345.             break;
  346.     
  347.     case ON:    bwidth += beffect;  fwidth += feffect;
  348.             fcount--;
  349.             break;
  350.  
  351.     case FWD:    fwidth += beffect + feffect;
  352.             fcount--;
  353.             break;
  354.     
  355.     default:    Error(INTERN, no_fpos, "BreakTable: illegal side");
  356.             break;
  357.     }
  358.  
  359.   } /* end while */
  360.  
  361.   back(x, COL) = bwidth;
  362.   fwd(x, COL) = fwidth;
  363.  
  364.   debug1(DOB, DD,  "] BreakTable returning %s", EchoObject(x));
  365.   debug2(DOB, DD, "  (size is %s, %s)", EchoLength(bwidth),EchoLength(fwidth));
  366.   return x;
  367. } /* end BreakTable */
  368.  
  369.  
  370. /*@::BreakObject()@***********************************************************/
  371. /*                                                                           */
  372. /*  OBJECT BreakObject(x, c)                                                 */
  373. /*                                                                           */
  374. /*  Break lines of object x so that it satisfies constraint c.               */
  375. /*                                                                           */
  376. /*****************************************************************************/
  377.  
  378. OBJECT BreakObject(x, c)
  379. OBJECT x;  CONSTRAINT *c;
  380. { OBJECT y;  CONSTRAINT yc;
  381.   debug3(DOB, DD,  "[ BreakObject(x (%s,%s),  %s), x =",
  382.     EchoLength(back(x, COL)), EchoLength(fwd(x, COL)), EchoConstraint(c));
  383.   ifdebug(DOB, DD, DebugObject(x));
  384.  
  385.   if( FitsConstraint(back(x, COL), fwd(x, COL), *c) )
  386.   { debug0(DOB, DD, "] BreakObject returning (fits).");
  387.     return x;
  388.   }
  389.   switch( type(x) )
  390.   {
  391.  
  392.     case ROTATE:
  393.     
  394.       Error(WARN, &fpos(x), "%s deleted (too wide; cannot break %s)",
  395.     KW_ROTATE, KW_ROTATE);
  396.       y = MakeWord(WORD, STR_EMPTY, &fpos(x));
  397.       back(y, COL) = fwd(y, COL) = 0;
  398.       ReplaceNode(y, x);
  399.       DisposeObject(x);
  400.       x = y;
  401.       break;
  402.  
  403.  
  404.     case SCALE:
  405.  
  406.       InvScaleConstraint(&yc, bc(constraint(x)), c);
  407.       Child(y, Down(x));
  408.       y = BreakObject(y, &yc);
  409.       back(x, COL) = (back(y, COL) * bc(constraint(x))) / SF;
  410.       fwd(x, COL) =  (fwd(y, COL)  * bc(constraint(x))) / SF;
  411.       break;
  412.  
  413.  
  414.     case WORD:
  415.     case QWORD:
  416.     
  417.       Error(WARN, &fpos(x), "word %s deleted (too wide)",string(x));
  418.       y = MakeWord(WORD, STR_EMPTY, &fpos(x));
  419.       back(y, COL) = fwd(y, COL) = 0;
  420.       ReplaceNode(y, x);
  421.       DisposeObject(x);
  422.       x = y;
  423.       break;
  424.  
  425.  
  426.     case WIDE:
  427.     
  428.       Error(WARN, &fpos(x), "%s %s reduced (too wide)",
  429.     EchoLength(bfc(constraint(x))), KW_WIDE);
  430.       MinConstraint(&constraint(x), c);
  431.       Child(y, Down(x));
  432.       y = BreakObject(y, &constraint(x));
  433.       back(x, COL) = back(y, COL);
  434.       fwd(x, COL) = fwd(y, COL);
  435.       EnlargeToConstraint(&back(x, COL), &fwd(x, COL), &constraint(x));
  436.       break;
  437.  
  438.  
  439.     case INCGRAPHIC:
  440.     case SINCGRAPHIC:
  441.  
  442.       Error(WARN, &fpos(x), "%s or %s deleted (too wide)", KW_INCGRAPHIC,
  443.     KW_SINCGRAPHIC);
  444.       y = MakeWord(WORD, STR_EMPTY, &fpos(x));
  445.       back(y, COL) = fwd(y, COL) = 0;
  446.       ReplaceNode(y, x);
  447.       DisposeObject(x);
  448.       x = y;
  449.       break;
  450.  
  451.  
  452.     case HIGH:
  453.     case VSCALE:
  454.     case HCONTRACT: 
  455.     case VCONTRACT:
  456.     case HEXPAND: 
  457.     case VEXPAND:
  458.     case PADJUST: 
  459.     case HADJUST: 
  460.     case VADJUST:
  461.     case ONE_ROW:
  462.     case ONE_COL:
  463.     
  464.       assert( Down(x) == LastDown(x), "BreakObject: downs!" );
  465.       Child(y, Down(x));
  466.       y = BreakObject(y, c);
  467.       back(x, COL) = back(y, COL);
  468.       fwd(x, COL) = fwd(y, COL);
  469.       break;
  470.  
  471.  
  472.     case GRAPHIC:
  473.     
  474.       Child(y, LastDown(x));
  475.       y = BreakObject(y, c);
  476.       back(x, COL) = back(y, COL);
  477.       fwd(x, COL) = fwd(y, COL);
  478.       break;
  479.  
  480.  
  481.     case SPLIT:
  482.     
  483.       Child(y, DownDim(x, COL));
  484.       y = BreakObject(y, c);
  485.       back(x, COL) = back(y, COL);
  486.       fwd(x, COL) = fwd(y, COL);
  487.       break;
  488.  
  489.  
  490.     case ACAT:
  491.     
  492.       x = FillObject(x, c);
  493.       break;
  494.  
  495.  
  496.     case HCAT:
  497.     
  498.       x = BreakTable(x, c);
  499.       break;
  500.  
  501.  
  502.     case COL_THR:
  503.     
  504.       BreakJoinedGroup(Down(x), LastDown(x), nil, c, &back(x,COL), &fwd(x,COL));
  505.       break;
  506.  
  507.  
  508.     case VCAT:
  509.     
  510.       x = BreakVcat(x, c);
  511.       break;
  512.             
  513.  
  514.     default:
  515.     
  516.       Error(INTERN, &fpos(x), "BreakObject: %s!", Image(type(x)) );
  517.       break;
  518.  
  519.   }
  520.   assert( back(x, COL) >= 0, "BreakObject: back(x, COL) < 0!" );
  521.   assert( fwd(x, COL) >= 0, "BreakObject: fwd(x, COL) < 0!" );
  522.   debug1(DOB, DD,  "] BreakObject returning %s", EchoObject(x));
  523.   debug2(DOB, DD, "  (size is %s, %s)",
  524.     EchoLength(back(x, COL)), EchoLength(fwd(x, COL)));
  525.   return x;
  526. } /* end BreakObject */
  527.