| /*@z20.c:Galley Flushing:DebugInnersNames()@**********************************/ |
| /* */ |
| /* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.24) */ |
| /* COPYRIGHT (C) 1991, 2000 Jeffrey H. Kingston */ |
| /* */ |
| /* Jeffrey H. Kingston (jeff@cs.usyd.edu.au) */ |
| /* Basser Department of Computer Science */ |
| /* The University of Sydney 2006 */ |
| /* AUSTRALIA */ |
| /* */ |
| /* This program is free software; you can redistribute it and/or modify */ |
| /* it under the terms of the GNU General Public License as published by */ |
| /* the Free Software Foundation; either Version 2, or (at your option) */ |
| /* any later version. */ |
| /* */ |
| /* This program is distributed in the hope that it will be useful, */ |
| /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ |
| /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ |
| /* GNU General Public License for more details. */ |
| /* */ |
| /* You should have received a copy of the GNU General Public License */ |
| /* along with this program; if not, write to the Free Software */ |
| /* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA */ |
| /* */ |
| /* FILE: z20.c */ |
| /* MODULE: Galley Flushing */ |
| /* EXTERNS: FlushGalley() */ |
| /* */ |
| /*****************************************************************************/ |
| #include "externs.h" |
| |
| #if DEBUG_ON |
| FULL_CHAR *DebugInnersNames(OBJECT inners) |
| { static FULL_CHAR buff[MAX_BUFF]; |
| OBJECT link, y, z; |
| StringCopy(buff, STR_EMPTY); |
| if( inners != nilobj ) |
| { for( link = Down(inners); link != inners; link = NextDown(link) ) |
| { Child(y, link); |
| if( link != Down(inners) ) StringCat(buff, STR_SPACE); |
| switch( type(y) ) |
| { |
| |
| case RECEIVING: |
| case UNATTACHED: |
| |
| assert( Down(y) != y, "DebugInnersNames: UNATTACHED!"); |
| Child(z, Down(y)); |
| StringCat(buff, SymName(actual(z))); |
| break; |
| |
| |
| case PRECEDES: |
| case GALL_PREC: |
| case DEAD: |
| |
| StringCat(buff, Image(type(y))); |
| break; |
| |
| |
| default: |
| |
| assert1(FALSE, "DebugInnersNames:", Image(type(y))); |
| break; |
| } |
| } |
| } |
| return buff; |
| } /* end DebugInnersNames */ |
| #endif |
| |
| |
| /*@::ParentFlush(), FlushGalley()@********************************************/ |
| /* */ |
| /* ParentFlush(prnt_flush, dest_index, kill) */ |
| /* */ |
| /* Flush the galley which is the parent of dest_index, if likely to flush. */ |
| /* If kill is TRUE, delete dest_index. */ |
| /* */ |
| /*****************************************************************************/ |
| |
| static void ParentFlush(BOOLEAN prnt_flush, OBJECT dest_index, BOOLEAN kill) |
| { OBJECT prnt; |
| debug3(DGF, DD, "ParentFlush(%s, %s, %s)", |
| bool(prnt_flush), EchoIndex(dest_index), bool(kill)); |
| if( prnt_flush ) |
| { Parent(prnt, Up(dest_index)); |
| if( kill ) DeleteNode(dest_index); |
| debug0(DGF, DD, " calling FlushGalley from ParentFlush"); |
| FlushGalley(prnt); |
| } |
| else if( kill ) DeleteNode(dest_index) |
| debug0(DGF, DD, "ParentFlush returning."); |
| } /* end ParentFlush */ |
| |
| |
| /*****************************************************************************/ |
| /* */ |
| /* FlushGalley(hd) */ |
| /* */ |
| /* Flush galley hd as far as possible. It could be the root galley. */ |
| /* */ |
| /*****************************************************************************/ |
| |
| void FlushGalley(OBJECT hd) |
| { OBJECT dest; /* the target galley hd empties into */ |
| OBJECT dest_index; /* the index of dest */ |
| OBJECT inners; /* list of galleys and PRECEDES to flush */ |
| OBJECT link, y; /* for scanning through the components of hd */ |
| int dim; /* direction of galley */ |
| CONSTRAINT dest_par_constr; /* the parallel size constraint on dest */ |
| CONSTRAINT dest_perp_constr; /* the perpendicular size constraint on dest */ |
| int pb, pf, f; /* candidate replacement sizes for dest */ |
| |
| OBJECT dest_encl; /* the VCAT or ACAT enclosing dest, if any */ |
| int dest_side; /* if dest_encl != nilobj, side dest is on */ |
| BOOLEAN need_adjust; /* TRUE as soon as dest_encl needs adjusting */ |
| FULL_LENGTH dest_back, dest_fwd; /* the current size of dest_encl or dest */ |
| FULL_LENGTH frame_size; /* the total constraint of dest_encl */ |
| OBJECT prec_gap; /* the gap preceding dest if any else nilobj */ |
| OBJECT prec_def; /* the component preceding dest, if any */ |
| OBJECT succ_gap; /* the gap following dest if any else nilobj */ |
| OBJECT succ_def; /* the component following dest, if any */ |
| OBJECT stop_link; /* most recently seen gap link of hd */ |
| FULL_LENGTH stop_back; /* back(dest_encl) incl all before stop_link */ |
| FULL_LENGTH stop_fwd; /* fwd(dest_encl) incl. all before stop_link */ |
| FULL_LENGTH stop_perp_back; /* back(dest_encl) in other direction */ |
| FULL_LENGTH stop_perp_fwd; /* fwd(dest_encl) in other direction */ |
| BOOLEAN prnt_flush; /* TRUE when the parent of hd needs a flush */ |
| BOOLEAN target_is_internal; /* TRUE if flushing into an internal target */ |
| BOOLEAN headers_seen; /* TRUE if a header is seen at all */ |
| OBJECT zlink, z, tmp, prnt; int attach_status; BOOLEAN remove_target; |
| OBJECT why; |
| FULL_LENGTH perp_back, perp_fwd; /* current perp size of dest_encl */ |
| |
| debug1(DGF, D, "[ FlushGalley %s (hd)", SymName(actual(hd))); |
| prnt_flush = FALSE; |
| dim = gall_dir(hd); |
| |
| RESUME: |
| assert( type(hd) == HEAD, "FlushGalley: type(hd) != HEAD!" ); |
| debug1(DGF, D, " resuming FlushGalley %s, hd =", SymName(actual(hd))); |
| ifdebugcond(DGF, DD, actual(hd) == nilobj, DebugGalley(hd, nilobj, 4)); |
| assert( Up(hd) != hd, "FlushGalley: resume found no parent to hd!" ); |
| |
| |
| /*@@************************************************************************/ |
| /* */ |
| /* The first step is to examine the parent of galley hd to determine the */ |
| /* status of the galley. If this is not suitable for flushing, we do */ |
| /* what we can to change the status. If still no good, return; so if */ |
| /* this code does not return, then the galley is ready to flush into a */ |
| /* destination in the normal way, and the following variables are set: */ |
| /* */ |
| /* dest_index the parent of the galley and index of its destination */ |
| /* dest the destination of the galley, a @Galley object */ |
| /* */ |
| /***************************************************************************/ |
| |
| Parent(dest_index, Up(hd)); |
| switch( type(dest_index) ) |
| { |
| |
| case DEAD: |
| |
| /* the galley has been killed off while this process was sleeping */ |
| debug1(DGF, D, "] FlushGalley %s returning (DEAD)", SymName(actual(hd))); |
| return; |
| |
| |
| case UNATTACHED: |
| |
| /* the galley is currently not attached to a destination */ |
| attach_status = AttachGalley(hd, &inners, &y); |
| debug1(DGF, DD, " ex-AttachGalley inners: %s", DebugInnersNames(inners)); |
| Parent(dest_index, Up(hd)); |
| switch( attach_status ) |
| { |
| |
| case ATTACH_KILLED: |
| |
| assert(inners==nilobj, "FlushGalley/ATTACH_KILLED: inners!=nilobj!"); |
| debug1(DGF, D, "] FlushGalley %s returning (ATTACH_KILLED)", |
| SymName(actual(hd))); |
| debug1(DGF, D, " prnt_flush = %s", bool(prnt_flush)); |
| return; |
| |
| |
| case ATTACH_INPUT: |
| |
| ParentFlush(prnt_flush, dest_index, FALSE); |
| assert(inners==nilobj, "FlushGalley/ATTACH_INPUT: inners!=nilobj!"); |
| debug1(DGF, D, "] FlushGalley %s returning (ATTACH_INPUT)", |
| SymName(actual(hd))); |
| return; |
| |
| |
| case ATTACH_NOTARGET: |
| |
| ParentFlush(prnt_flush, dest_index, FALSE); |
| assert(inners==nilobj, "FlushGalley/ATTACH_NOTARG: inners!=nilobj!"); |
| debug1(DGF, D, "] FlushGalley %s returning (ATTACH_NOTARGET)", |
| SymName(actual(hd))); |
| return; |
| |
| |
| case ATTACH_SUSPEND: |
| |
| /* AttachGalley only returns inners here if they really need to */ |
| /* be flushed; in particular the galley must be unsized before */ |
| if( inners != nilobj ) |
| { |
| debug0(DGF, DD, " calling FlushInners() from FlushGalley (a)"); |
| FlushInners(inners, nilobj); |
| goto RESUME; |
| } |
| stop_link = nilobj; |
| goto SUSPEND; /* nb y will be set by AttachGalley in this case */ |
| |
| |
| case ATTACH_NULL: |
| |
| /* hd will have been linked to the unexpanded target in this case */ |
| remove_target = (actual(actual(dest_index)) == whereto(hd)); |
| if( force_gall(hd) ) |
| { |
| /* if hd is a forcing galley, close all predecessors */ |
| debug3(DGA, D, " forcing ATTACH_NULL case for %s into %s (%s)", |
| SymName(actual(hd)), SymName(whereto(hd)), |
| remove_target ? "remove_target" : "not remove_target"); |
| Parent(prnt, Up(dest_index)); |
| if( !non_blocking(dest_index) && remove_target ) |
| { |
| /* *** |
| prnt_flush = TRUE; |
| *** */ |
| prnt_flush = non_blocking(dest_index) = TRUE; |
| } |
| FreeGalley(prnt, Up(dest_index), &inners, Up(dest_index), |
| whereto(hd)); |
| } |
| else |
| { |
| debug3(DGA, D, " non-force ATTACH_NULL case for %s into %s (%s)", |
| SymName(actual(hd)), SymName(whereto(hd)), |
| remove_target ? "remove_target" : "not remove_target"); |
| if( blocked(dest_index) && remove_target ) prnt_flush = TRUE; |
| } |
| DetachGalley(hd); |
| KillGalley(hd, TRUE); |
| if( inners != nilobj ) |
| { |
| debug0(DGF, DD, " calling FlushInners() from FlushGalley (b)"); |
| FlushInners(inners, nilobj); |
| } |
| else ParentFlush(prnt_flush, dest_index, remove_target); |
| debug0(DGF, D, "] FlushGalley returning ATTACH_NULL"); |
| return; |
| |
| |
| case ATTACH_ACCEPT: |
| |
| /* if hd is a forcing galley, or actual(dest_index) is */ |
| /* @ForceGalley, then close all predecessors */ |
| if( force_gall(hd) || actual(actual(dest_index)) == ForceGalleySym ) |
| { Parent(prnt, Up(dest_index)); |
| debug1(DGA, D, " forcing ATTACH_ACCEPT case for %s", |
| SymName(actual(hd))); |
| /* debug0(DGA, DD, " force: prnt ="); */ |
| /* ifdebug(DGA, DD, DebugObject(prnt)); */ |
| /* debug1(DGA, D," calling FreeGalley from FlushGalley(%s)", */ |
| /* SymName(actual(hd))); */ |
| if( !non_blocking(dest_index) ) prnt_flush = TRUE; /* bug fix */ |
| FreeGalley(prnt, Up(dest_index), &inners, Up(dest_index), |
| whereto(hd)); |
| /* debug0(DGA, DD, " force: after FreeGalley, prnt ="); */ |
| /* ifdebug(DGA, DD, DebugObject(prnt)); */ |
| } |
| else prnt_flush = prnt_flush || blocked(dest_index); |
| debug1(DGF, DD, " force: prnt_flush = %s", bool(prnt_flush)); |
| if( inners != nilobj ) |
| { |
| debug0(DGF, DD, " calling FlushInners() from FlushGalley (c)"); |
| FlushInners(inners, nilobj); |
| } |
| goto RESUME; |
| |
| |
| default: |
| |
| assert(FALSE, "FlushGalley: attach_status"); |
| break; |
| |
| } |
| break; |
| |
| |
| case RECEIVING: |
| |
| if( actual(actual(dest_index)) == InputSym ) |
| { ParentFlush(prnt_flush, dest_index, FALSE); |
| debug1(DGF, D, "] FlushGalley %s retn, input", SymName(actual(hd))); |
| return; |
| } |
| break; |
| |
| |
| default: |
| |
| assert1(FALSE, "FlushGalley: dest_index", Image(type(dest_index))); |
| break; |
| } |
| dest = actual(dest_index); |
| if( underline(dest) == UNDER_UNDEF ) underline(dest) = UNDER_OFF; |
| target_is_internal = |
| (dim==ROWM && !external_ver(dest)) || (dim==COLM && !external_hor(dest)); |
| headers_seen = FALSE; |
| debug1(DGF, DD, " dest_index: %s", EchoObject(dest_index)); |
| |
| |
| /*@@************************************************************************/ |
| /* */ |
| /* The second step is to examine the components of the galley one by one */ |
| /* to determine if they can be promoted. Each component has the format */ |
| /* */ |
| /* { <index> } <object> */ |
| /* */ |
| /* and is always followed by a gap object (except the last component). */ |
| /* An index indicates that the following object has some interesting */ |
| /* feature, and it points to that feature inside the object. There are */ |
| /* two possible actions for each component, in addition to accepting it: */ |
| /* */ |
| /* REJECT: The component does not fit, so detach the galley */ |
| /* SUSPEND: The component is incomplete; go to sleep and wait */ |
| /* */ |
| /***************************************************************************/ |
| |
| stop_link = dest_encl = inners = nilobj; |
| need_adjust = FALSE; |
| |
| /***************************************************************************/ |
| /* */ |
| /* Loop invariant */ |
| /* */ |
| /* The children of hd up to but not including Child(link) have been */ |
| /* examined and pronounced to be promotable, if unbreakable gaps are */ |
| /* ignored. When unbreakable gaps are taken into account, the most */ |
| /* recent gap where a break is possible is at Child(stop_link), or */ |
| /* nowhere if stop_link == nilobj. */ |
| /* */ |
| /* Case 1: target_is_internal == FALSE */ |
| /* */ |
| /* If this flag is FALSE, it means that the target of this galley is */ |
| /* external. Consequently, there is no need to calculate sizes because */ |
| /* there is no constraint on them. Also, a REJECT action is impossible */ |
| /* so unbreakable gaps are no impediment. Variable dest_encl is nilobj. */ |
| /* */ |
| /* Case 2: target_is_internal == TRUE */ |
| /* */ |
| /* If this flag is TRUE, it means that the target of this galley is */ |
| /* internal. Consequently, sizes need to be calculated, and unbreakable */ |
| /* gaps need to be taken into account. Variable dest_encl may be not */ |
| /* nilobj, in which case the following variables are defined: */ |
| /* */ |
| /* dest_encl the object enclosing dest (which must exist) */ |
| /* prec_gap gap object preceding dest (which must exist) */ |
| /* prec_def first definite object preceding dest (must exist) */ |
| /* dest_back back(dest_encl) including effect of accepted compts */ |
| /* dest_fwd fwd(dest_encl) including effect of accepted compts */ |
| /* dest_side BACK or FWD, i.e. which side of the mark dest is on */ |
| /* dest_par_constr the parallel size constraint on dest */ |
| /* dest_perp_constr the perpendicular size constraint on dest */ |
| /* frame_size size of frame enclosing dest_encl */ |
| /* perp_back back(dest_encl) in other direction, incl accepteds */ |
| /* perp_fwd fwd(dest_encl) in other direction, incl accepteds */ |
| /* */ |
| /* if dest_encl is nilobj, these variables are not defined. */ |
| /* */ |
| /* If stop_link is non-nilobj, then in the internal case dest_encl must */ |
| /* be non-nilobj, and the following variables are defined: */ |
| /* */ |
| /* stop_back back(dest_encl) including all before stop_link */ |
| /* stop_fwd fwd(dest_encl) including all before stop_link */ |
| /* stop_perp_back back(dest_encl) in other direction */ |
| /* stop_perp_fwd fwd(dest_encl) in other direction */ |
| /* */ |
| /* need_adjust is true if at least one definite component has been */ |
| /* accepted for promotion and the destination is internal; hence, */ |
| /* dest_encl is defined and its size needs to be adjusted. */ |
| /* */ |
| /* inners is the set of all PRECEDES and UNATTACHED indexes found. */ |
| /* */ |
| /***************************************************************************/ |
| |
| for( link = Down(hd); link != hd; link = NextDown(link) ) |
| { |
| Child(y, link); |
| if( type(y) == SPLIT ) Child(y, DownDim(y, dim)); |
| debug2(DGF, DD, " examining %s %s", Image(type(y)), EchoObject(y)); |
| switch( type(y) ) |
| { |
| |
| case GAP_OBJ: |
| |
| underline(y) = underline(dest); |
| prec_gap = y; |
| if( target_is_internal ) |
| { |
| /* *** not necessarily true |
| assert( dest_encl != nilobj, "FlushGalley/GAP_OBJ: dest_encl!" ); |
| *** */ |
| if( dest_encl != nilobj && !nobreak(gap(prec_gap)) ) |
| { |
| stop_link = link; |
| stop_back = dest_back; |
| stop_fwd = dest_fwd; |
| stop_perp_back = perp_back; |
| stop_perp_fwd = perp_fwd; |
| } |
| } |
| else stop_link = link; |
| if( !join(gap(y)) ) seen_nojoin(hd) = TRUE; |
| break; |
| |
| |
| case SCALE_IND: |
| case COVER_IND: |
| case EXPAND_IND: |
| case GALL_PREC: |
| case GALL_FOLL: |
| case GALL_FOLL_OR_PREC: |
| case GALL_TARG: |
| case CROSS_PREC: |
| case CROSS_FOLL: |
| case CROSS_FOLL_OR_PREC: |
| case CROSS_TARG: |
| case PAGE_LABEL_IND: |
| |
| underline(y) = underline(dest); |
| break; |
| |
| |
| case PRECEDES: |
| case UNATTACHED: |
| |
| if( inners == nilobj ) New(inners, ACAT); |
| Link(inners, y); |
| break; |
| |
| |
| case RECEIVING: |
| case RECEPTIVE: |
| |
| goto SUSPEND; |
| |
| |
| case FOLLOWS: |
| |
| Child(tmp, Down(y)); |
| if( Up(tmp) == LastUp(tmp) ) |
| { link = PrevDown(link); |
| DisposeChild(NextDown(link)); |
| break; |
| } |
| Parent(tmp, Up(tmp)); |
| assert(type(tmp) == PRECEDES, "Flush: PRECEDES!"); |
| switch( CheckComponentOrder(tmp, dest_index) ) |
| { |
| case CLEAR: DeleteNode(tmp); |
| link = PrevDown(link); |
| DisposeChild(NextDown(link)); |
| break; |
| |
| case PROMOTE: break; |
| |
| case BLOCK: goto SUSPEND; |
| |
| case CLOSE: if( opt_components(hd) != nilobj ) |
| { DisposeObject(opt_components(hd)); |
| opt_components(hd) = nilobj; |
| debug2(DOG, D, "FlushGalley(%s) de-optimizing %s", |
| "(CLOSE problem)", SymName(actual(hd))); |
| } |
| debug1(DGF, DD, " reject (a) %s", EchoObject(y)); |
| goto REJECT; |
| } |
| break; |
| |
| |
| case BEGIN_HEADER: |
| case END_HEADER: |
| case SET_HEADER: |
| case CLEAR_HEADER: |
| |
| /* do nothing except take note, until actually promoted out of here */ |
| headers_seen = TRUE; |
| break; |
| |
| |
| case NULL_CLOS: |
| case PAGE_LABEL: |
| case WORD: |
| case QWORD: |
| case ONE_COL: |
| case ONE_ROW: |
| case WIDE: |
| case HIGH: |
| case HSHIFT: |
| case VSHIFT: |
| case HSCALE: |
| case VSCALE: |
| case HCOVER: |
| case VCOVER: |
| case HCONTRACT: |
| case VCONTRACT: |
| case HLIMITED: |
| case VLIMITED: |
| case HEXPAND: |
| case VEXPAND: |
| case START_HVSPAN: |
| case START_HSPAN: |
| case START_VSPAN: |
| case HSPAN: |
| case VSPAN: |
| case ROTATE: |
| case BACKGROUND: |
| case SCALE: |
| case KERN_SHRINK: |
| case INCGRAPHIC: |
| case SINCGRAPHIC: |
| case PLAIN_GRAPHIC: |
| case GRAPHIC: |
| case LINK_SOURCE: |
| case LINK_DEST: |
| case ACAT: |
| case HCAT: |
| case VCAT: |
| case ROW_THR: |
| case CLOSURE: |
| case CROSS: |
| case FORCE_CROSS: |
| |
| underline(y) = underline(dest); |
| if( dim == ROWM ) |
| { |
| /* make sure y is not joined to a target below (vertical case only) */ |
| for( zlink = NextDown(link); zlink != hd; zlink = NextDown(zlink) ) |
| { Child(z, zlink); |
| switch( type(z) ) |
| { |
| case RECEPTIVE: |
| case RECEIVING: y = z; |
| goto SUSPEND; |
| |
| case GAP_OBJ: if( !join(gap(z)) ) zlink = PrevDown(hd); |
| break; |
| |
| default: break; |
| } |
| } |
| |
| /* try vertical hyphenation before anything else */ |
| if( type(y) == HCAT ) VerticalHyphenate(y); |
| |
| } |
| |
| /* check size constraint */ |
| if( target_is_internal ) |
| { |
| /* initialise dest_encl etc if not done yet */ |
| if( dest_encl == nilobj ) |
| { assert( UpDim(dest,1-dim) == UpDim(dest,dim), "FlushG: UpDims!" ); |
| /* *** weird old code, trying for UpDim(dest, ROWM)? |
| Parent(dest_encl, NextDown(Up(dest))); |
| *** */ |
| Parent(dest_encl, Up(dest)); |
| debug4(DGF, DD, " flush dest = %s %s, dest_encl = %s %s", |
| Image(type(dest)), EchoObject(dest), |
| Image(type(dest_encl)), EchoObject(dest_encl)); |
| assert( (dim==ROWM && type(dest_encl)==VCAT) || |
| (dim==COLM && type(dest_encl)==ACAT), |
| "FlushGalley: dest != VCAT or ACAT!" ); |
| SetNeighbours(Up(dest), FALSE, &prec_gap, &prec_def, |
| &succ_gap, &succ_def, &dest_side); |
| assert(prec_gap != nilobj || is_indefinite(type(y)), |
| "FlushGalley: prec_gap == nilobj && !is_indefinite(type(y))!" ); |
| assert(succ_gap == nilobj, "FlushGalley: succ_gap != nilobj!" ); |
| assert(dest_side == FWD || is_indefinite(type(y)), |
| "FlushGalley: dest_side != FWD || !is_indefinite(type(y))!"); |
| dest_back = back(dest_encl, dim); |
| dest_fwd = fwd(dest_encl, dim); |
| perp_back = back(dest_encl, 1-dim); |
| perp_fwd = fwd(dest_encl, 1-dim); |
| Constrained(dest_encl, &dest_par_constr, dim, &why); |
| Constrained(dest_encl, &dest_perp_constr, 1-dim, &why); |
| debug1(DGF, DD, " setting dest_perp_constr = %s", |
| EchoConstraint(&dest_perp_constr)); |
| frame_size = constrained(dest_par_constr) ? bfc(dest_par_constr) :0; |
| } |
| |
| if( !is_indefinite(type(y)) ) |
| { |
| ifdebugcond(DGF, DD, mode(gap(prec_gap)) == NO_MODE, |
| DebugGalley(hd, y, 4)); |
| |
| /* calculate parallel effect of adding y to dest */ |
| f = dest_fwd + fwd(y, dim) - fwd(prec_def, dim) + |
| ActualGap(fwd(prec_def, dim), back(y, dim), |
| fwd(y, dim), &gap(prec_gap), frame_size, |
| dest_back + dest_fwd - fwd(prec_def, dim)); |
| debug5(DGF, DD, " f = %s + %s - %s + %s (prec_gap %s)", |
| EchoLength(dest_fwd), EchoLength(fwd(y, dim)), |
| EchoLength(fwd(prec_def, dim)), EchoLength( |
| ActualGap(fwd(prec_def, dim), back(y, dim), |
| fwd(y, dim), &gap(prec_gap), frame_size, |
| dest_back + dest_fwd - fwd(prec_def, dim)) |
| ), EchoGap(&gap(prec_gap))); |
| debug3(DGF, DD, " b,f: %s,%s; dest_encl: %s", |
| EchoLength(dest_back), EchoLength(f), |
| EchoConstraint(&dest_par_constr)); |
| |
| /* check new size against parallel constraint */ |
| if( (units(gap(prec_gap))==FRAME_UNIT && width(gap(prec_gap)) > FR) |
| || !FitsConstraint(dest_back, f, dest_par_constr) |
| || (opt_components(hd) != nilobj && opt_comps_permitted(hd)<=0) |
| ) |
| { |
| if( opt_components(hd) != nilobj ) |
| { OBJECT z; |
| |
| /* record the size of this just-completed target area for hd */ |
| New(z, WIDE); |
| CopyConstraint(constraint(z), dest_par_constr); |
| Link(opt_constraints(hd), z); |
| ifdebug(DOG, D, |
| debug2(DOG, D, "FlushGalley(%s) adding constraint %s", |
| SymName(actual(hd)), EchoConstraint(&constraint(z))); |
| if( units(gap(prec_gap))==FRAME_UNIT && |
| width(gap(prec_gap)) > FR ) |
| { debug1(DOG, D, " prec_gap = %s", EchoGap(&gap(prec_gap))); |
| } |
| if( !FitsConstraint(dest_back, f, dest_par_constr) ) |
| { debug3(DOG, D, " !FitsConstraint(%s, %s, %s)", |
| EchoLength(dest_back), EchoLength(f), |
| EchoConstraint(&dest_par_constr)); |
| } |
| if( opt_comps_permitted(hd) <= 0 ) |
| { debug1(DOG, D, " opt_comps_permitted = %2d", |
| opt_comps_permitted(hd)); |
| } |
| debug4(DOG, D, "prec_gap = %s; y = %s (%s,%s):", |
| EchoGap(&gap(prec_gap)), Image(type(y)), |
| EchoLength(back(y, dim)), EchoLength(fwd(y, dim))); |
| DebugObject(y); |
| ) |
| |
| /* refresh the number of components permitted into the next target */ |
| if( opt_counts(hd) != nilobj && Down(opt_counts(hd)) != opt_counts(hd) ) |
| { Child(z, Down(opt_counts(hd))); |
| opt_comps_permitted(hd) += comp_count(z) - 1; |
| DisposeChild(Up(z)); |
| } |
| else opt_comps_permitted(hd) = MAX_FILES; /* a large number */ |
| debug1(DOG, D, " REJECT permitted = %2d", opt_comps_permitted(hd)); |
| } |
| debug1(DGF, DD, " reject (b) %s", EchoObject(y)); |
| goto REJECT; |
| } |
| |
| /* calculate perpendicular effect of adding y to dest */ |
| if( seen_nojoin(hd) ) |
| { |
| pb = 0; |
| pf = find_max(perp_fwd, size(y, 1-dim)); |
| } |
| else |
| { |
| pb = find_max(perp_back, back(y, 1-dim)); |
| pf = find_max(perp_fwd, fwd(y, 1-dim)); |
| } |
| |
| /* check new size against perpendicular constraint */ |
| if( !FitsConstraint(pb, pf, dest_perp_constr) ) |
| { |
| if( opt_components(hd) != nilobj ) |
| { DisposeObject(opt_components(hd)); |
| opt_components(hd) = nilobj; |
| debug1(DOG, D, "FlushGalley(%s) de-optimizing (perp problem)", |
| SymName(actual(hd))); |
| } |
| if( dim == ROWM ) |
| { |
| Error(20, 3, "component too wide for available space", |
| WARN, &fpos(y)); |
| debug6(DGF, DD, " %s,%s [%s,%s] too wide for %s, y = %s", |
| EchoLength(pb), EchoLength(pf), |
| EchoLength(back(y, 1-dim)), EchoLength(fwd(y, 1-dim)), |
| EchoConstraint(&dest_perp_constr), EchoObject(y)); |
| } |
| debug1(DGF, DD, " reject (c) %s", EchoObject(y)); |
| goto REJECT; |
| } |
| |
| /* accept definite component */ |
| dest_fwd = f; prec_def = y; |
| perp_back = pb; perp_fwd = pf; |
| need_adjust = TRUE; |
| if( opt_components(hd) != nilobj ) |
| { opt_comps_permitted(hd)--; |
| debug1(DOG, D, " ACCEPT permitted = %2d", opt_comps_permitted(hd)); |
| } |
| } |
| /* accept indefinite component */ |
| } /* end if( target_is_internal ) */ |
| |
| /* accept this component into dest, subject to following nobreaks */ |
| debug3(DGF, DD, " t-accept %s %s %s", Image(type(y)), EchoObject(y), |
| EchoFilePos(&fpos(y))); |
| prnt_flush = prnt_flush || blocked(dest_index); |
| debug1(DGF, DDD, " prnt_flush = %s", bool(prnt_flush)); |
| debug1(DGF, DDD, " inners = %s", DebugInnersNames(inners)); |
| if( inners != nilobj ) |
| { BOOLEAN promotable; OBJECT tgp; |
| |
| /* We would prefer to promote right now, then give these inners */ |
| /* a chance. However this is not possible unless the following */ |
| /* gap (if any) is breakable */ |
| |
| if( type(NextDown(link)) == LINK ) |
| { Child(tgp, NextDown(link)); |
| assert( type(tgp) == GAP_OBJ, "FlushGalley: tgp!" ); |
| promotable = !nobreak(gap(tgp)); |
| } |
| else promotable = TRUE; |
| |
| if( promotable ) |
| { |
| debug0(DGS, D, "calling Promote(hd, stop_link) from FlushGalley (ACCEPT)"); |
| Promote(hd, NextDown(link), dest_index, TRUE); |
| if( need_adjust ) |
| { debug0(DSA, D, " calling AdjustSize from FlushGalley (ACCEPT)"); |
| AdjustSize(dest_encl, dest_back, dest_fwd, dim); |
| AdjustSize(dest_encl, perp_back, perp_fwd, 1-dim); |
| } |
| debug0(DGF, DD, " calling FlushInners() from FlushGalley (d)"); |
| FlushInners(inners, hd); |
| goto RESUME; |
| } |
| } |
| break; |
| |
| |
| default: |
| |
| assert1(FALSE, "FlushGalley:", Image(type(y))); |
| break; |
| |
| } /* end switch */ |
| } /* end for */ |
| |
| |
| /* EMPTY: */ |
| |
| /* galley is now completely accepted; clean up and exit */ |
| debug0(DGF, DD, " galley empty now"); |
| if( inners != nilobj ) DisposeObject(inners); |
| if( Down(hd) != hd ) |
| { |
| debug0(DGS, D, "calling Promote(hd, hd) from FlushGalley (EMPTY)"); |
| Promote(hd, hd, dest_index, TRUE); |
| if( need_adjust ) |
| { debug0(DSA, D, " calling AdjustSize from FlushGalley (EMPTY)"); |
| AdjustSize(dest_encl, dest_back, dest_fwd, dim); |
| AdjustSize(dest_encl, perp_back, perp_fwd, 1-dim); |
| } |
| } |
| if( opt_components(hd) != nilobj ) |
| { OBJECT z; |
| New(z, WIDE); |
| if( dest_encl != nilobj ) |
| CopyConstraint(constraint(z), dest_par_constr); |
| else |
| SetConstraint(constraint(z), |
| MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH); |
| Link(opt_constraints(hd), z); |
| debug2(DOG, D, "FlushGalley(%s) empty adding constraint %s", |
| SymName(actual(hd)), EchoConstraint(&constraint(z))); |
| } |
| DetachGalley(hd); |
| debug0(DGF, DD, " calling KillGalley from FlushGalley"); |
| KillGalley(hd, TRUE); |
| ParentFlush(prnt_flush, dest_index, TRUE); |
| debug1(DGF,D,"] FlushGalley %s returning (emptied).", SymName(actual(hd))); |
| return; |
| |
| |
| REJECT: |
| |
| /* reject this component and move to a new dest; at this point, link is */ |
| /* the link to the rejected component; its child is either y or else it */ |
| /* is a SPLIT whose child is y */ |
| debug3(DGF, D, "at REJECT now (stop_link %s); headers(%s) = %s", |
| stop_link != nilobj ? "non-nil" : "nil", |
| SymName(actual(hd)), EchoObject(headers(hd))); |
| assert(actual(dest) != PrintSym, "FlushGalley: reject print!"); |
| if( inners != nilobj ) DisposeObject(inners); |
| if( stop_link != nilobj ) |
| { |
| debug0(DGS, D, "calling Promote(hd, stop_link) from FlushGalley (REJECT)"); |
| Promote(hd, stop_link, dest_index, TRUE); |
| if( need_adjust ) |
| { debug0(DSA, D, " calling AdjustSize from FlushGalley (REJECT)"); |
| AdjustSize(dest_encl, stop_back, stop_fwd, dim); |
| AdjustSize(dest_encl, stop_perp_back, stop_perp_fwd, 1-dim); |
| } |
| } |
| |
| /* if headers_seen, handle any headers not already handled by Promote() */ |
| if( target_is_internal && headers_seen ) |
| { OBJECT z, zlink, top_z; |
| for( zlink = hd; NextDown(zlink) != link; ) |
| { |
| Child(z, NextDown(zlink)); |
| top_z = z; |
| debug2(DGF, D, "FlushGalley(%s)/REJECT header-examining %s", |
| SymName(actual(hd)), EchoObject(z)); |
| if( type(z) == SPLIT ) |
| Child(z, DownDim(z, dim)); |
| if( is_header(type(z)) ) |
| { |
| assert(top_z == z, "FlushGalley: header under SPLIT!"); |
| HandleHeader(hd, z); |
| } |
| else |
| zlink = NextDown(zlink); |
| } |
| } |
| |
| /* now, if there are headers, dump them into the galley */ |
| if( headers(hd) != nilobj ) |
| { int headers_count; |
| |
| /* dump new copy of headers into top of galley */ |
| assert(Down(headers(hd))!=headers(hd), "FlushGalley/REJECT: headers!"); |
| tmp = Down(hd); |
| assert( tmp != hd, "FlushGalley/REJECT: first_link!" ); |
| headers_count = 0; |
| for( link=Down(headers(hd)); link != headers(hd); link=NextDown(link) ) |
| { Child(y, link); |
| debug2(DGS, D, "FlushGalley(%s)/REJECT linking %s", |
| SymName(actual(hd)), EchoObject(y)); |
| assert(type(y)!=COL_THR && type(y)!=ROW_THR, "FlushGalley/REJECT THR!"); |
| Link(tmp, y); |
| headers_count++; |
| } |
| assert(headers_count % 2 == 0, "FlushGalley/REJECT: headers_count!"); |
| } |
| |
| /* now detach and resume */ |
| DetachGalley(hd); |
| assert( type(dest_index) == RECEIVING, "FlushGalley/REJECT: dest_index!" ); |
| prnt_flush = prnt_flush || blocked(dest_index); |
| DeleteNode(dest_index); |
| goto RESUME; |
| |
| |
| SUSPEND: |
| |
| /* suspend this component */ |
| debug1(DGF, D, " suspend %s", EchoIndex(y)); |
| if( inners != nilobj ) DisposeObject(inners); |
| if( stop_link != nilobj ) |
| { |
| debug0(DGS, D, "calling Promote(hd, stop_link) from FlushGalley/SUSPEND"); |
| Promote(hd, stop_link, dest_index, TRUE); |
| if( need_adjust ) |
| { debug0(DSA, D, " calling AdjustSize from FlushGalley (SUSPEND)"); |
| AdjustSize(dest_encl, stop_back, stop_fwd, dim); |
| AdjustSize(dest_encl, stop_perp_back, stop_perp_fwd, 1-dim); |
| } |
| } |
| |
| /* check whether external galleys can remove the blockage */ |
| if( type(y) == RECEPTIVE && ready_galls(hd) != nilobj && AllowCrossDb ) |
| { OBJECT eg, val, index2, hd2, tag, seq, newsym; |
| BOOLEAN found, gall; FULL_CHAR newtag[MAX_BUFF], newseq[MAX_BUFF]; |
| |
| /* get first ready galley in from cross reference database */ |
| Child(eg, Down(ready_galls(hd))); |
| SwitchScope(nilobj); |
| val = ReadFromFile(eg_fnum(eg), eg_fpos(eg), eg_lnum(eg)); |
| UnSwitchScope(nilobj); |
| if( val == nilobj ) |
| Error(20, 1, "error in database file %s", |
| FATAL, &fpos(y), FileName(eg_fnum(eg))); |
| assert( type(val) == CLOSURE, "AttachG: db CLOSURE!" ); |
| New(index2, UNATTACHED); |
| pinpoint(index2) = nilobj; |
| New(hd2, HEAD); |
| FposCopy(fpos(hd2), fpos(val)); |
| actual(hd2) = actual(val); |
| limiter(hd2) = nilobj; |
| opt_components(hd2) = opt_constraints(hd2) = nilobj; |
| gall_dir(hd2) = horiz_galley(actual(val)); |
| sized(hd2) = FALSE; |
| ready_galls(hd2) = nilobj; |
| must_expand(hd2) = TRUE; |
| Link(index2, hd2); |
| Link(hd2, val); |
| SetTarget(hd2); |
| foll_or_prec(hd2) = GALL_FOLL; |
| enclose_obj(hd2) = (has_enclose(actual(hd2)) ? BuildEnclose(hd2) : nilobj); |
| headers(hd2) = dead_headers(hd2) = nilobj; |
| Link(Up(y), index2); |
| |
| /* set up the next ready galley for reading next time */ |
| Child(tag, Down(eg)); Child(seq, LastDown(eg)); |
| do /* skip duplicate seq values */ |
| { found = DbRetrieveNext(OldCrossDb, &gall, &newsym, newtag, newseq, |
| &eg_fnum(eg), &eg_fpos(eg), &eg_lnum(eg), &eg_cont(eg)); |
| debug2(DGF, DD, " ext gall found: %15s gall: %15s", |
| bool(gall), bool(found)); |
| debug2(DGF, DD, " ext gall new sym: %15s old sym: %15s", |
| SymName(newsym), SymName(eg_symbol(eg))); |
| debug2(DGF, DD, " ext gall new tag: %15s old tag: %15s", |
| newtag, string(tag)); |
| debug2(DGF, DD, " ext gall new seq: %15s old seq: %15s", |
| newseq, string(seq)); |
| if( found ) found = gall && newsym == eg_symbol(eg) && |
| StringEqual(newtag, string(tag)); |
| |
| /* merge galleys whose seq strings are equal */ |
| if( found && StringEqual(newseq, string(seq)) ) |
| { |
| SwitchScope(nilobj); |
| val = ReadFromFile(eg_fnum(eg), eg_fpos(eg), eg_lnum(eg)); |
| UnSwitchScope(nilobj); |
| if( val == nilobj ) |
| Error(20, 2, "error in database file %s", |
| FATAL, &fpos(y), FileName(eg_fnum(eg))); |
| assert( type(val) == CLOSURE, "AttachG: db CLOSURE!" ); |
| if( !has_merge(actual(val)) ) DisposeObject(val); |
| else /* add val to hd2 */ |
| { if( type(hd2) != ACAT ) |
| { OBJECT tmp = hd2; |
| New(hd2, ACAT); |
| MoveLink(Up(tmp), hd2, CHILD); |
| Link(hd2, tmp); |
| } |
| Link(hd2, val); |
| } |
| } |
| |
| } while( found && StringEqual(newseq, string(seq)) ); |
| if( found ) |
| { DisposeChild(Up(tag)); |
| DisposeChild(Up(seq)); |
| tag = MakeWord(WORD, newtag, no_fpos); |
| seq = MakeWord(WORD, newseq, no_fpos); |
| Link(eg, tag); Link(eg, seq); |
| debug1(DGF, DD, " another ext gall: into %s", SymName(newsym)); |
| } |
| else |
| { DisposeChild(Up(eg)); |
| debug1(DGF, DD, " last ext gall into ", SymName(eg_symbol(eg))); |
| if( Down(ready_galls(hd)) == ready_galls(hd) ) |
| { Dispose(ready_galls(hd)); |
| ready_galls(hd) = nilobj; |
| debug0(DGF, DD, " all ext galls exhausted"); |
| } |
| } |
| |
| /* flush the ready galley found above, and resume */ |
| debug2(DGF, DD, " ext gall FlushGalley (%s into %s)", |
| SymName(actual(hd2)), SymName(whereto(hd2))); |
| debug0(DGF, DD, " calling FlushGalley from FlushGalley/SUSPEND"); |
| if( type(hd2) == ACAT ) |
| hd2 = ConvertGalleyList(hd2); |
| FlushGalley(hd2); |
| goto RESUME; |
| } |
| else if( type(y) == RECEPTIVE && trigger_externs(y) && AllowCrossDb ) |
| { OBJECT sym, cr, ins, tag, seq, eg, cnt; BOOLEAN found; |
| FULL_CHAR newseq[MAX_BUFF]; FILE_NUM tfnum; long tfpos, tcont; |
| int tlnum; |
| debug1(DGF, DD, " ext gall target %s", SymName(actual(actual(y)))); |
| for( sym = FirstExternTarget(actual(actual(y)), &cnt); |
| sym != nilobj; sym = NextExternTarget(actual(actual(y)), &cnt) ) |
| { |
| debug1(DGF, DD, " ext gall gall_targ %s", SymName(sym)); |
| cr = GallTargEval(sym, &fpos(actual(y))); |
| New(ins, GALL_TARG); |
| actual(ins) = cr; |
| Link(Up(y), ins); |
| Child(tag, LastDown(cr)); |
| assert( is_word(type(tag)), "FlushGalley: cr is_word(type(tag))!" ); |
| found = DbRetrieve(OldCrossDb, TRUE, sym, string(tag), |
| newseq, &tfnum, &tfpos, &tlnum, &tcont); |
| if( found ) |
| { if( ready_galls(hd) == nilobj ) New(ready_galls(hd), ACAT); |
| New(eg, EXT_GALL); |
| debug1(DGF, DD, " ext gall retrieved: into %s", SymName(sym)); |
| eg_fnum(eg) = tfnum; |
| eg_fpos(eg) = tfpos; |
| eg_lnum(eg) = tlnum; |
| eg_symbol(eg) = sym; |
| eg_cont(eg) = tcont; |
| tag = MakeWord(WORD, string(tag), no_fpos); |
| Link(eg, tag); |
| seq = MakeWord(WORD, newseq, no_fpos); |
| Link(eg, seq); |
| Link(ready_galls(hd), eg); |
| } |
| } |
| trigger_externs(y) = FALSE; |
| if( ready_galls(hd) != nilobj ) goto RESUME; |
| } /* end if external galleys */ |
| |
| /* if non-blocking, delete the index and resume */ |
| if( type(y) == RECEPTIVE && non_blocking(y) ) |
| { DeleteNode(y); |
| goto RESUME; |
| } |
| else if( type(y) == RECEIVING && non_blocking(y) ) |
| { |
| if( Down(y) == y ) |
| { DeleteNode(y); |
| } |
| else |
| { Child(z, Down(y)); |
| if( opt_components(z) != nilobj ) |
| GazumpOptimize(z, actual(y)); |
| DetachGalley(z); |
| } |
| goto RESUME; |
| } |
| |
| /* if all the above fail to remove the blockage, suspend */ |
| blocked(y) = TRUE; |
| ParentFlush(prnt_flush, dest_index, FALSE); |
| debug1(DGF,D, "] FlushGalley %s returning (suspend)", SymName(actual(hd))); |
| return; |
| |
| } /* end FlushGalley */ |