/*@z22.c:Galley Service:Interpose()@******************************************/ /* */ /* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.41) */ /* COPYRIGHT (C) 1991, 2023 Jeffrey H. Kingston */ /* */ /* Jeffrey H. Kingston (jeff@it.usyd.edu.au) */ /* School of Information Technologies */ /* 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 3, 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: z22.c */ /* MODULE: Galley Service */ /* EXTERNS: Interpose(), FlushInners(), ExpandRecursives(), */ /* Promote(), KillGalley(), FreeGalley(), */ /* SetTarget(), CheckComponentOrder() */ /* */ /*****************************************************************************/ #include "externs.h" #include "parent.h" #include "child.h" /* these three variables refer to the root galley only */ static BOOLEAN first = TRUE; /* if first component unwritten */ static OBJECT page_label = nilobj; /* current page label object */ static OBJECT prev_page_label = nilobj; /* previous page label object */ /*****************************************************************************/ /* */ /* void PromoteInit(void) */ /* */ /* Initialize this module. */ /* */ /*****************************************************************************/ void PromoteInit(void) { first = TRUE; page_label = nilobj; prev_page_label = nilobj; } /*****************************************************************************/ /* */ /* Interpose(z, typ, x, y) */ /* */ /* Insert a new typ object above z. Its sizes are to be taken from x */ /* (column) and y (row). */ /* */ /*****************************************************************************/ void Interpose(OBJECT z, int typ, OBJECT x, OBJECT y) { OBJECT encl; New(encl, typ); FposCopy(fpos(encl), fpos(y)); ReplaceNode(encl, z); Link(encl, z); back(encl, COLM) = back(x, COLM); fwd(encl, COLM) = fwd(x, COLM); back(encl, ROWM) = back(y, ROWM); fwd(encl, ROWM) = fwd(y, ROWM); underline(encl) = underline(z); } /* end Interpose */ /*@::FlushInners()@***********************************************************/ /* */ /* FlushInners(inners, hd) */ /* */ /* Flush each galley on the list inners. These have become flushable */ /* by being promoted off the top of galley hd; if hd is the root galley, */ /* identifiable by having PrintSym as target, do not flush inners at all. */ /* */ /*****************************************************************************/ void FlushInners(OBJECT inners, OBJECT hd) { OBJECT y, z, tmp, dest_index; debug1(DGA, D, "[ FlushInners(%s, -)", DebugInnersNames(inners)); ifdebug(DGF, D, OBJECT link; fprintf(stderr, "dgf: [ FlushInners("); for( link = Down(inners); link != inners; link = NextDown(link) ) { Child(y, link); fprintf(stderr, " %s", Image(type(y))); switch( type(y) ) { case DEAD: break; case RECEIVING: case UNATTACHED: if( Down(y) != y ) /* bug fix (was assert before) */ { assert( Down(y) != y, "FlushInners: UNATTACHED!"); Child(z, Down(y)); fprintf(stderr, " %s", SymName(actual(z))); } break; case PRECEDES: break; case GALL_PREC: break; default: break; } } fprintf(stderr, ")"); debug0(DGF, D, ""); ) /* check for root galley case */ if( hd != nilobj ) { assert( Up(hd) != hd, "FlushInners: Up(hd)!" ); Parent(dest_index, Up(hd)); if( actual(actual(dest_index)) == PrintSym ) { DisposeObject(inners); debug0(DGA, D, "] FlushInners returning (PrintSym)"); debug0(DGF, D, "] FlushInners returning (PrintSym)"); return; } } while( Down(inners) != inners ) { Child(y, Down(inners)); DeleteLink(Down(inners)); debug2(DGA, D, "FlushInners at %s (remainder %s)", Image(type(y)), DebugInnersNames(inners)); switch( type(y) ) { case DEAD: break; case RECEIVING: case UNATTACHED: if( Down(y) != y ) /* bug fix (was assert before) */ { assert( Down(y) != y, "FlushInners: UNATTACHED!"); Child(z, Down(y)); debug1(DGA,D," possibly calling FlushGalley %s from FlushInners (a)", SymName(actual(z))); if( whereto(z) != nilobj ) debug2(DGA,D," (whereto(z) = %s, uses_extern_target = %s)", SymName(whereto(z)), bool(uses_extern_target(whereto(z)))); if( whereto(z)==nilobj || !uses_extern_target(whereto(z)) ) /* &&& */ FlushGalley(z); } break; case PRECEDES: Child(tmp, Down(y)); if( Up(tmp) != LastUp(tmp) ) { Parent(tmp, LastUp(tmp)); assert(type(tmp)==FOLLOWS, "FlushInners: FOLLOWS!"); if( blocked(tmp) ) { blocked(tmp) = FALSE; Parent(z, Up(tmp)); debug0(DGF, D, " calling FlushGalley from FlushInners (b)"); if( whereto(z)==nilobj || !uses_extern_target(whereto(z)) )/* &&& */ FlushGalley(z); } } break; case GALL_PREC: /* someone else is looking after this now */ break; default: assert1(FALSE, "FlushInners:", Image(type(y))); break; } } Dispose(inners); debug0(DGA, D, "] FlushInners returning"); debug0(DGF, D, "] FlushInners returning"); } /* end FlushInners */ /*@::ExpandRecursives()@******************************************************/ /* */ /* ExpandRecursives(recs) */ /* */ /* Expand each of the recursive definite objects in the list recs. */ /* */ /*****************************************************************************/ void ExpandRecursives(OBJECT recs) { CONSTRAINT non_c, hc, vc; OBJECT target_index, target, z = nilobj, n1, inners, newrecs, hd, tmp, env, why; debug0(DCR, DDD, "ExpandRecursives(recs)"); SetConstraint(non_c, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH); n1 = nilobj; assert(recs != nilobj, "ExpandRecursives: recs == nilobj!"); while( Down(recs) != recs ) { Child(target_index, Down(recs)); DeleteLink( Down(recs) ); assert( type(target_index) == RECURSIVE, "ExpandRecursives: index!" ); target = actual(target_index); debug2(DCR, DDD, " expanding %s %s", Image(type(target_index)), EchoObject(target)); /* expand body of target, convert to galley, and check size */ New(hd, HEAD); actual(hd) = actual(target); must_expand(hd) = TRUE; force_gall(hd) = FALSE; ClearHeaders(hd); enclose_obj(hd) = limiter(hd) = nilobj; opt_components(hd) = opt_constraints(hd) = nilobj; gall_dir(hd) = horiz_galley(actual(target)); whereto(hd) = ready_galls(hd) = nilobj; foll_or_prec(hd) = GALL_FOLL; sized(hd) = FALSE; seen_nojoin(hd) = FALSE; tmp = CopyObject(target, &fpos(target)); env = DetachEnv(tmp); Link(hd, tmp); Link(target_index, hd); SizeGalley(hd, env, external_ver(target), gall_dir(hd) == ROWM ? threaded(target) : FALSE, FALSE, FALSE, &save_style(target), &non_c, nilobj, &n1, &newrecs, &inners, nilobj); debug1(DGA, D, " ExpandRecursives inners: %s", DebugInnersNames(inners)); debug0(DCR, DDD, " as galley:"); ifdebug(DCR, DDD, DebugObject(hd)); debug1(DGS, DD, "[ ExpandRecursives calling Constrained(%s, COLM)", EchoObject(target)); Constrained(target, &hc, COLM, &why); debug2(DGS, DD, "] ExpandRecursives Constrained(%s, COLM) = %s", EchoObject(target), EchoConstraint(&hc)); debug3(DCR, DDD, " horizontal size: (%s, %s); constraint: %s", EchoLength(back(hd, COLM)), EchoLength(fwd(hd, COLM)), EchoConstraint(&hc)); if( !FitsConstraint(back(hd, COLM), fwd(hd, COLM), hc) ) { DisposeChild(Up(hd)); if( inners != nilobj ) DisposeObject(inners); if( newrecs != nilobj ) DisposeObject(newrecs); DeleteNode(target_index); debug0(DCR, DDD, " rejecting (too wide)"); continue; } if( !external_ver(target) ) { Constrained(target, &vc, ROWM, &why); debug2(DSC, DD, "Constrained( %s, ROWM ) = %s", EchoObject(target), EchoConstraint(&vc)); Child(z, LastDown(hd)); debug3(DCR, DDD, " vsize: (%s, %s); constraint: %s", EchoLength(back(z, ROWM)), EchoLength(fwd(z, ROWM)), EchoConstraint(&vc)); if( !FitsConstraint(back(z, ROWM), fwd(z, ROWM), vc) ) { DisposeChild(Up(hd)); if( inners != nilobj ) DisposeObject(inners); if( newrecs != nilobj ) DisposeObject(newrecs); DeleteNode(target_index); debug0(DCR, DDD, " rejecting (too high)"); continue; } } /* object fits; adjust sizes and promote */ debug0(DSA, D, "calling AdjustSize from ExpandRecursives (a)"); AdjustSize(target, back(hd, COLM), fwd(hd, COLM), COLM); if( !external_ver(target) ) { debug0(DSA, D, "calling AdjustSize from ExpandRecursives (b)"); AdjustSize(target, back(z, ROWM), fwd(z, ROWM), ROWM); Interpose(target, VCAT, z, z); } debug0(DGS, DD, "calling Promote(hd, hd) from ExpandRecursives"); Promote(hd, hd, target_index, TRUE); DeleteNode(hd); DeleteNode(target_index); if( inners != nilobj ) { debug0(DGF, D, " calling FlushInners from ExpandRecursives"); FlushInners(inners, nilobj); } if( newrecs != nilobj ) MergeNode(recs, newrecs); } /* end while */ Dispose(recs); debug0(DCR, DDD, "ExpandRecursives returning."); } /* end ExpandRecursives */ /*@::FindSplitInGalley()@*****************************************************/ /* */ /* static OBJECT FindSplitInGalley(hd) */ /* */ /* Search simply joined galley hd for a SPLIT object, which must be there. */ /* */ /*****************************************************************************/ static OBJECT FindSplitInGalley(OBJECT hd) { OBJECT link, y = nilobj; debug0(DGF, D, "FindSplitInGalley(hd)"); for( link = Down(hd); link != hd; link = NextDown(link) ) { Child(y, link); if( is_definite(type(y)) ) break; } if( link == hd ) { debug0(DGF, D, "FindSplitInGalley failing, no definite component; hd ="); ifdebug(DGF, D, DebugObject(hd)); Error(22, 1, "FindSplit: missing galley component", INTERN, &fpos(hd)); } while( type(y) != SPLIT ) switch( type(y) ) { case VCAT: case ONE_ROW: case WIDE: case HIGH: case HSHIFT: case VSHIFT: case VCONTRACT: case VLIMITED: case VEXPAND: Child(y, Down(y)); break; case BEGIN_HEADER: case SET_HEADER: Child(y, LastDown(y)); break; case CLOSURE: case NULL_CLOS: case END_HEADER: case CLEAR_HEADER: case PAGE_LABEL: case HCAT: case WORD: case QWORD: case ACAT: case ROW_THR: case COL_THR: case ONE_COL: case SCALE: case KERN_SHRINK: case HMIRROR: case VMIRROR: case HSCALE: case VSCALE: case HCOVER: case VCOVER: case HCONTRACT: case HLIMITED: case HEXPAND: case START_HVSPAN: case START_HSPAN: case START_VSPAN: case HSPAN: case VSPAN: case ROTATE: case BACKGROUND: case INCGRAPHIC: case SINCGRAPHIC: case PLAIN_GRAPHIC: case GRAPHIC: case LINK_SOURCE: case LINK_DEST: case LINK_DEST_NULL: case LINK_URL: debug0(DGF, D, "FindSplitInGalley(hd) failing, hd ="); ifdebug(DGF, D, DebugObject(hd)); Error(22, 2, "FindSplitInGalley failed", INTERN, &fpos(y),Image(type(y))); break; default: assert1(FALSE, "FindSplitInGalley:", Image(type(y))); break; } debug0(DGF, D, "FindSplitInGalley returning."); return y; } /* end FindSplitInGalley */ /*****************************************************************************/ /* */ /* ClearHeaders(OBJECT hd) */ /* */ /* Clear the headers of hd. */ /* */ /*****************************************************************************/ void ClearHeaders(OBJECT hd) { int i; for( i = 0; i < MAX_HCOPIES; i++ ) headers(hd, i) = nilobj; head_next(hd) = 0; } /* end ClearHeaders */ /*****************************************************************************/ /* */ /* DisposeHeaders(OBJECT hd) */ /* */ /* Dispose the headers of hd. */ /* */ /*****************************************************************************/ static void DisposeHeaders(OBJECT hd) { int i; for( i = 0; i < MAX_HCOPIES; i++ ) { if( headers(hd, i) != nilobj ) { assert(type(headers(hd, i)) == ACAT || type(headers(hd, i)) == VCAT, "DisposeHeaders: type(headers(hd))!"); while( Down(headers(hd, i)) != headers(hd, i) ) { DisposeChild(Down(headers(hd, i))); } headers(hd, i) = nilobj; } } } /* end DisposeHeaders */ /*@::HandleHeader()@**********************************************************/ /* */ /* HandleHeader(hd, link, header) */ /* */ /* Given galley hd containing header object header, this routine handles */ /* the header, i.e. it updates headers(hd) to reflect the effect of the */ /* header, then deletes the header and its following gap object. */ /* */ /* Link is the link from hd to the header (it may actually link to a */ /* split object which then links to the header, but no matter). */ /* */ /* Actually, we no longer allow a SPLIT object above a header, because */ /* that means there is a COL_THR or ROW_THR above it and when we dispose */ /* the header, Dispose is not able to dispose the appropriate child of the */ /* COL_THR or ROW_THR. So Manifest() must not insert a SPLIT above a */ /* header, and we check for that. */ /* */ /*****************************************************************************/ void HandleHeader(OBJECT hd, OBJECT header) { OBJECT g, gaplink, gap_obj; int i; debug3(DGS, D, "[ HandleHeader(%s, %s); headers = %s; galley:", SymName(actual(hd)), EchoObject(header), EchoObject(headers(hd, head_next(hd)))); ifdebug(DGS, DD, DebugGalley(hd, nilobj, 2)); assert(is_header(type(header)), "HandleHeader: type(header)!"); assert(Up(header) == LastUp(header) && Up(header) != header, "HandleHeader: header parents!"); switch( type(header) ) { case CLEAR_HEADER: /* clear out old headers, if any */ DisposeHeaders(hd); break; case SET_HEADER: /* clear out old headers (not safe to dispose them yet), if any */ DisposeHeaders(hd); /* NB NO BREAK! */ case BEGIN_HEADER: Child(gap_obj, Down(header)); for( i = 0; i < MAX_HCOPIES; i++ ) { /* make new headers if required */ if( headers(hd, i) == nilobj ) New(headers(hd, i), gall_dir(hd) == ROWM ? VCAT : ACAT); /* construct a gap object from the left parameter */ New(g, GAP_OBJ); underline(g) = FALSE; Link(g, CopyObject(gap_obj, &fpos(gap_obj))); GapCopy(gap(g), line_gap(save_style(header))); mark(gap(g)) = join(gap(g)) = FALSE; /* move header and gap into headers() */ MoveLink(NextDown(Down(header)), headers(hd, i), PARENT); Link(headers(hd, i), g); debug2(DGS, D, "HandleHeader moving %s header %s", SymName(actual(hd)), Image(type(header))); } break; case END_HEADER: for( i = 0; i < MAX_HCOPIES; i++ ) { if( headers(hd, i) == nilobj ) Error(22, 11, "no header for %s to remove", WARN, &fpos(hd), KW_END_HEADER); else { /* dispose last gap */ assert(LastDown(headers(hd, i))!=headers(hd, i), "Promote/END_HEADER!"); Child(g, LastDown(headers(hd, i))); assert(type(g) == GAP_OBJ, "HandleHeader: END_HEADER/gap!"); DisposeChild(LastDown(headers(hd, i))); /* dispose last header object */ assert(LastDown(headers(hd, i))!=headers(hd, i), "Promote/END_HEADER!"); DisposeChild(LastDown(headers(hd, i))); if( Down(headers(hd, i)) == headers(hd, i) ) { DisposeObject(headers(hd, i)); headers(hd, i) = nilobj; } } } break; } /* dispose header object (must take care to disentangle safely) */ gaplink = NextDown(Up(header)); assert(type(gaplink) == LINK, "HandleHeader: type(gaplink)!"); if( type(header) == CLEAR_HEADER || type(header) == END_HEADER ) { /* first disentangle child properly */ assert(Down(header) != header && Down(header) == LastDown(header), "HH!"); DisposeChild(Down(header)); } DisposeChild(Up(header)); DisposeChild(gaplink); debug2(DGS, D, "] HandleHeader returning; headers(%s) now %s; galley:", SymName(actual(hd)), EchoObject(headers(hd, 0))); ifdebug(DGS, DD, DebugGalley(hd, nilobj, 2)); } /* end HandleHeader */ /*@::Promote()@***************************************************************/ /* */ /* Promote(hd, stop_link, dest_index, join_after) */ /* */ /* Promote components of galley hd into its destination (dest), up to but */ /* not including the one linked to hd by link stop_link, which always */ /* follows a component. No size adjustments are made, except that when */ /* two col_thr nodes are merged, a COLM adjustment is made to the result. */ /* */ /* If the galley is ending here, Promote inserts a gap at the end of it. */ /* Whether to make this a joining gap or not is a tricky question which */ /* Promote answers by referring to join_after. */ /* */ /* Any BEGIN_HEADER, END_HEADER, SET_HEADER, or CLEAR_HEADER objects in */ /* the promoted components are diverted to headers(hd), if the target */ /* is internal. */ /* */ /*****************************************************************************/ void Promote(OBJECT hd, OBJECT stop_link, OBJECT dest_index, BOOLEAN join_after) { OBJECT dest, link, y, z, tmp1, tmp2, why, top_y; FULL_CHAR *label_string, buff[MAX_LINE]; FULL_LENGTH aback, afwd; int dim, pnval; debug2(DGS, DD, "[ Promote(%s width %s, stop_link):", SymName(actual(hd)), EchoLength(size(hd, COLM))); ifdebug(DGS, DD, DebugGalley(hd, stop_link, 2)); assert( type(hd) == HEAD, "Promote: hd!" ); assert( type(stop_link) == LINK || stop_link == hd, "Promote: stop_link!" ); assert( stop_link != Down(hd), "Promote: stop_link == Down(hd)!" ); type(dest_index) = RECEIVING; dest = actual(dest_index); /* insert final gap if galley is ending */ if( stop_link != hd ) { Child(y, stop_link); if( type(y) != GAP_OBJ ) { ifdebug(DGS, DD, DebugGalley(hd, stop_link, 2)); } assert( type(y) == GAP_OBJ, "Promote: missing GAP_OBJ!" ); stop_link = NextDown(stop_link); } else { New(y, GAP_OBJ); FposCopy(fpos(y), fpos(hd)); hspace(y) = 0; vspace(y) = 1; /* SetGap(gap(y), FALSE, FALSE, seen_nojoin(hd), FIXED_UNIT, NO_MODE, 0); */ /* SetGap(gap(y), FALSE, FALSE, threaded(dest), FIXED_UNIT, NO_MODE, 0); */ /* SetGap(gap(y), FALSE, FALSE, TRUE, FIXED_UNIT, NO_MODE, 0); */ /* ClearGap(gap(y)); */ SetGap(gap(y), FALSE, FALSE, join_after, FIXED_UNIT, NO_MODE, 0); Link(stop_link, y); } /* if optimizing, add to dummy paragraph containing components and gaps */ if( opt_components(hd) != nilobj ) { OBJECT last, tmp; debug1(DOG, DD, "Promote(%s) optimizing:", SymName(actual(hd))); if( LastDown(opt_components(hd))!=opt_components(hd) && !opt_gazumped(hd) ) { Child(last, LastDown(opt_components(hd))); } else last = nilobj; for( link = Down(hd); link != stop_link; link = NextDown(link) ) { Child(y, link); if( type(y) == GAP_OBJ ) { if( last == nilobj ) { /* do nothing, gap cannot separate definite objects */ debug1(DOG, DD, " skipping initial GAP_OBJ %s", EchoGap(&gap(y))); } else if( type(last) == GAP_OBJ ) { /* previous gap must have preceded an indefinite, so overwrite it */ FposCopy(fpos(last), fpos(y)); debug2(DOG, DD, " overwriting GAP_OBJ %s with %s", EchoGap(&gap(last)), EchoGap(&gap(y))); GapCopy(gap(last), gap(y)); if( Down(last) != last ) DisposeChild(Down(last)); if( Down(y) != y ) { Child(tmp, Down(y)); tmp = CopyObject(tmp, no_fpos); Link(last, tmp); } join(gap(last)) = TRUE; /* irrelevant but improves debug output */ } else { /* previous was definite, so this gap must be stored */ opt_gazumped(hd) = FALSE; New(last, GAP_OBJ); FposCopy(fpos(last), fpos(y)); GapCopy(gap(last), gap(y)); join(gap(last)) = TRUE; /* irrelevant but improves debug output */ hspace(last) = 1; vspace(last) = 0; Link(opt_components(hd), last); debug1(DOG, DD, " adding GAP_OBJ %s", EchoGap(&gap(last))); } } else if( is_word(type(y)) ) { /* definite, must be stored */ opt_gazumped(hd) = FALSE; last = MakeWord(type(y), string(y), &fpos(y)); back(last, COLM) = back(y, gall_dir(hd)); fwd(last, COLM) = fwd(y, gall_dir(hd)); word_font(last) = word_font(y); word_colour(last) = word_colour(y); word_underline_colour(last) = word_underline_colour(y); word_texture(last) = word_texture(y); word_outline(last) = word_outline(y); word_language(last) = word_language(y); word_baselinemark(last) = word_baselinemark(y); word_strut(last) = word_strut(y); word_ligatures(last) = word_ligatures(y); word_hyph(last) = word_hyph(y); Link(opt_components(hd), last); debug2(DOG, DD, " adding %s \"%s\"", Image(type(last)), string(last)); } else if( is_indefinite(type(y)) ) { /* indefinite, always skip these */ } else if( is_definite(type(y)) ) { /* definite other than WORD, add it */ opt_gazumped(hd) = FALSE; last = MakeWord(QWORD, AsciiToFull("w"), &fpos(y)); back(last, COLM) = back(y, gall_dir(hd)); fwd(last, COLM) = fwd(y, gall_dir(hd)); Link(opt_components(hd), last); debug1(DOG, DD, " adding word for %s", EchoObject(y)); } } debug1(DOG, DD, "Promote(%s) end optimizing", SymName(actual(hd))); } /* error if promoting a seen_nojoin galley into a threaded destination */ if( seen_nojoin(hd) && gall_dir(hd) == ROWM && threaded(dest) ) Error(22, 3, "galley %s must have a single column mark", FATAL, &fpos(hd), SymName(actual(hd))); /* make nojoin status clear by adding an extra gap at start if needed */ if( gall_dir(hd) == ROWM && !external_ver(dest) && seen_nojoin(hd) && join(gap(y)) ) { OBJECT prnt, extra_null, extra_gap; /* add nojoin gap at start */ Parent(prnt, Up(dest)); /* can't be threaded */ assert( type(prnt) == VCAT, "Promote: nojoin case, can't find VCAT" ); New(extra_null, NULL_CLOS); back(extra_null, COLM) = fwd(extra_null, COLM) = 0; back(extra_null, ROWM) = fwd(extra_null, ROWM) = 0; New(extra_gap, GAP_OBJ); hspace(extra_gap) = vspace(extra_gap) = 0; SetGap(gap(extra_gap), FALSE, FALSE, FALSE, FIXED_UNIT, EDGE_MODE, 0); Link(Down(prnt), extra_gap); Link(Down(prnt), extra_null); debug0(DGS, DD, " Promote adding extra nojoin gap"); /* join(gap(y)) = FALSE; */ } /* if promoting out of root galley, do special things */ if( actual(dest) == PrintSym ) { CONSTRAINT c; link = hd; while( NextDown(link) != stop_link ) { Child(y, NextDown(link)); debug2(DGS, DD, "root promote %s %s", Image(type(y)), is_definite(type(y)) ? STR_EMPTY : EchoObject(y)); if( type(y) == SPLIT ) Child(y, DownDim(y, ROWM)); switch( type(y) ) { case SCALE_IND: case COVER_IND: case PRECEDES: DisposeChild(NextDown(link)); break; case UNATTACHED: assert( Down(y) != y, "FlushRootGalley: UNATTACHED!" ); Child(z, Down(y)); assert( type(z) == HEAD, "FlushRootGalley: unattached HEAD!" ); if( sized(z) ) { /* galley is part flushed, leave it here */ link = NextDown(link); } /* ??? else if( foll_or_prec(z) == GALL_PREC ) */ else if( foll_or_prec(z) == GALL_PREC || foll_or_prec(z) == GALL_FOLL_OR_PREC ) { /* galley is preceding or foll_or_prec, send to CrossSequence */ OBJECT t; /* type(y) = GALL_PREC; */ type(y) = foll_or_prec(z); pinpoint(y) = nilobj; Child(t, Down(z)); /* actual(y) = CrossMake(whereto(z), t, GALL_PREC); */ actual(y) = CrossMake(whereto(z), t, type(y)); DisposeChild(Down(y)); CrossSequence(actual(y)); DisposeChild(NextDown(link)); } else { /* galley was never attached, print message and kill it */ Error(22, 4, "galley %s deleted (never attached)", WARN, &fpos(z), SymName(actual(z))); debug1(DGF, D, "never-attached galley %s:", EchoFilePos(&fpos(z))); ifdebug(DGF, D, DebugObject(z)); KillGalley(z, FALSE); /* *** link = NextDown(link); *** */ } break; case EXPAND_IND: /* expand @HExpand or @VExpand to occupy everything possible */ dim = type(actual(y)) == HEXPAND ? COLM : ROWM; Constrained(actual(y), &c, dim, &why); if( constrained(c) ) { FULL_LENGTH b = back(actual(y), dim); FULL_LENGTH f = fwd(actual(y), dim); EnlargeToConstraint(&b, &f, &c); debug1(DSA, D, "Promote %s AdjustSize", Image(type(actual(y)))); AdjustSize(actual(y), b, f, dim); } DisposeChild(NextDown(link)); break; case PAGE_LABEL_IND: if( page_label != nilobj ) { DisposeObject(page_label); page_label = nilobj; } Child(z, Down(y)); assert( type(z) == PAGE_LABEL, "Promote: type(z) != PAGE_LABEL!" ); assert( Down(z) != z, "Promote: PAGE_LABEL Down(z) == z!" ); Child(page_label, Down(z)); DeleteLink(Up(page_label)); DisposeChild(NextDown(link)); break; case CROSS_PREC: case CROSS_FOLL: case CROSS_FOLL_OR_PREC: case CROSS_TARG: debug2(DGS, DD, "root promote %s %s", Image(type(y)), EchoObject(y)); /* NB NO BREAK */ case GALL_PREC: case GALL_FOLL: case GALL_FOLL_OR_PREC: case GALL_TARG: CrossSequence(actual(y)); DisposeChild(NextDown(link)); break; case BEGIN_HEADER: case END_HEADER: case SET_HEADER: case CLEAR_HEADER: Error(22, 10, "%s symbol ignored (out of place)", WARN, &fpos(y), Image(type(y))); DisposeChild(NextDown(link)); break; case WORD: case QWORD: case ONE_COL: case ONE_ROW: case WIDE: case HIGH: case HSHIFT: case VSHIFT: case HMIRROR: case VMIRROR: 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 LINK_URL: case ACAT: case HCAT: case ROW_THR: case CLOSURE: case NULL_CLOS: case LINK_DEST_NULL: case PAGE_LABEL: case CROSS: case FORCE_CROSS: /* print this component */ debug0(DGS, DD, "root promote definite or indefinite"); if( !is_indefinite(type(y)) && size(y, ROWM) != 0 ) { /* fix horizontally; work out which fonts needed */ SetLengthDim(COLM); debug3(DGP,D, " Promote calling FixAndPrint %s %s,%s", dimen(COLM), EchoLength(back(y,COLM)), EchoLength(fwd(y, COLM))); FixAndPrintObject(y, back(y, COLM), back(y, COLM), fwd(y, COLM), COLM, FALSE, 0, 0, &aback, &afwd); /* get label string from page_label, or prev_page_label + 1 */ if( page_label != nilobj && is_word(type(page_label)) ) label_string = string(page_label); else if( prev_page_label!=nilobj && is_word(type(prev_page_label)) && sscanf( (char *) string(prev_page_label), "%d", &pnval)==1 ) { sprintf( (char *) buff, "%d", pnval + 1); label_string = buff; } else label_string = AsciiToFull("?"); /* print prefatory or page separating material, including fonts */ debug1(DGS, DD, "root promote definite; label_string = %s", label_string); debug1(DCR, DD, "label_string = %s", label_string); if( first ) { BackEnd->PrintBeforeFirstPage(size(hd, COLM), size(y, ROWM), label_string); first = FALSE; } else BackEnd->PrintBetweenPages(size(hd, COLM), size(y, ROWM), label_string); /* dispose prev_page_label and move page_label to prev_page_label */ if( prev_page_label != nilobj ) DisposeObject(prev_page_label); prev_page_label = page_label; page_label = nilobj; /* fix and print vertically */ debug1(DGF,D, " Promote calling FixAndPrint %s", Image(type(y))); debug3(DGP,D, " Promote calling FixAndPrint %s %s,%s", dimen(ROWM), EchoLength(back(y,ROWM)), EchoLength(fwd(y, ROWM))); SetLengthDim(ROWM); FixAndPrintObject(y, back(y,ROWM), back(y, ROWM), fwd(y, ROWM), ROWM, FALSE, size(y, ROWM), 0, &aback, &afwd); } DisposeChild(NextDown(link)); /* scavenge any filter files now not needed */ FilterScavenge(FALSE); break; case GAP_OBJ: DisposeChild(NextDown(link)); break; default: assert1(FALSE, "Promote:", Image(type(y))); break; } } debug0(DGS, DD, "Promote returning (root galley)."); return; } /* prepare the promotion */ if( external_ver(dest) && gall_dir(hd) == ROWM ) { if( threaded(dest) ) { Parent(tmp1, UpDim(dest, COLM)); assert( type(tmp1) == COL_THR, "Promote: tmp1 not COL_THR!" ); y = FindSplitInGalley(hd); assert( type(y) == SPLIT, "Promote: FindSplitInGalley!" ); Child(tmp2, DownDim(y, COLM)); assert( type(tmp2) == COL_THR, "Promote: tmp2 not COL_THR!" ); if( tmp1 != tmp2 ) { FULL_LENGTH b = find_max(back(tmp1, COLM), back(tmp2, COLM)); FULL_LENGTH f = find_max(fwd(tmp1, COLM), fwd(tmp2, COLM)); debug0(DSA, D, "calling AdjustSize(tmp1) from Promote (node merging)"); AdjustSize(tmp1, b, f, COLM); debug0(DSA, D, "calling AdjustSize(tmp2) from Promote (node merging)"); AdjustSize(tmp2, b, f, COLM); MergeNode(tmp1, tmp2); } } link = Up(dest_index); } else if( external_hor(dest) && gall_dir(hd) == COLM ) { link = Up(dest_index); } else { dim = gall_dir(hd); for( link = hd; NextDown(link) != stop_link; ) { Child(y, NextDown(link)); debug1(DGS, DD, "ordinary promote examining %s", EchoObject(y)); top_y = y; if( type(y) == SPLIT ) Child(y, DownDim(y, dim)); if( is_header(type(y)) ) { assert(top_y == y, "Promote: header under SPLIT!"); HandleHeader(hd, y); } else if( is_index(type(y)) ) MoveLink(NextDown(link), Up(dest_index), PARENT); else link = NextDown(link); } assert( Down(hd) != stop_link, "Promote: Down(hd) == stop_link!" ); assert( UpDim(dest, ROWM) == UpDim(dest, COLM), "Promote: dims!" ); link = Up(dest); } /* promote components */ TransferLinks(Down(hd), stop_link, link); debug0(DGS, DD, "] Promote returning; galley:"); ifdebug(DGS, DD, DebugGalley(hd, nilobj, 2)); } /* end Promote */ /*@::MakeDead(), KillGalley()@************************************************/ /* */ /* static MakeDead(y) */ /* */ /* Convert object y into a DEAD object and remove it to the dead store. */ /* */ /*****************************************************************************/ static void MakeDead(OBJECT y) { static int dead_count = 0; /* number of DEAD objects seen */ static OBJECT dead_store = nilobj; /* where DEAD objects are kept */ debug1(DGS, DDD, "MakeDead( %s )", Image(type(y))); if( dead_store == nilobj ) New(dead_store, ACAT); type(y) = DEAD; MoveLink(Up(y), dead_store, PARENT); if( dead_count >= 150 ) { DisposeChild(Down(dead_store)); } else dead_count++; debug1(DGS, DDD, "MakeDead returning (dead_count = %d).", dead_count); } /* end MakeDead */ /*****************************************************************************/ /* */ /* KillGalley(hd, optimize) */ /* */ /* Kill galley hd, which may be sized or unsized. The index of hd must */ /* be UNATTACHED; it is moved out of its present location to a secret spot. */ /* */ /* If hd is to be optimized, generate all the stuff for the cross */ /* reference database. However, don't do this if optimize is FALSE, for */ /* in that case hd is defective in some way and not optimizable. */ /* */ /*****************************************************************************/ void KillGalley(OBJECT hd, BOOLEAN optimize) { OBJECT prnt, link, y, z; debug2(DGF, D, "[ KillGalley(Galley %s into %s)", SymName(actual(hd)), SymName(whereto(hd))); assert( type(hd) == HEAD && Up(hd) != hd, "KillGalley: precondition!" ); Parent(prnt, Up(hd)); assert( type(prnt) == UNATTACHED, "KillGalley: UNATTACHED precondition!" ); assert( Up(prnt) != prnt, "KillGalley: prnt!" ); /* delete any ready_galls that might be hanging about */ if( ready_galls(hd) != nilobj ) { DisposeObject(ready_galls(hd)); ready_galls(hd) = nilobj; } /* delete every remaining component */ for( link = hd; NextDown(link) != hd; ) { Child(y, NextDown(link)); switch( type(y) ) { case RECEIVING: while( Down(y) != y ) { Child(z, Down(y)); DetachGalley(z); } DeleteNode(y); break; case RECEPTIVE: assert( Down(y) == y, "KillGalley: RECEPTIVE!" ); DeleteNode(y); break; case UNATTACHED: assert( Down(y) != y, "KillGalley: UNATTACHED!" ); Child(z, Down(y)); KillGalley(z, FALSE); break; case HEAD: assert(FALSE, "KillGalley: head"); break; default: DisposeChild(NextDown(link)); break; } } /* perform optimization calculations if required */ if( optimize && opt_components(hd) != nilobj ) CalculateOptimize(hd); /* move index into dead_store */ MakeDead(prnt); debug0(DGF, D, "] KillGalley returning."); } /* end KillGalley */ /*@::FreeGalley()@************************************************************/ /* */ /* FreeGalley(hd, stop_link, inners, relocate_link, sym) */ /* */ /* Free galley hd up to but not including stop_link. *Inners is well- */ /* defined, either nilobj or an ACAT of galleys to be flushed. */ /* */ /* Relocate_link defines what to do with any galley attached to one of the */ /* freed targets. If it is non-nilobj, galley hd is searched onwards from */ /* it to see if a target can be found there. If so, the galley is */ /* relocated to just before that point. If not, or if relocate_link is */ /* nilobj, the galley is freed and added to *inners for flushing. If the */ /* whereto() of such galley is sym, it is freed, not relocated, because the */ /* cause of this call to FreeGalley is also targeted to sym, and it will */ /* consume all possible targets of sym. */ /* */ /*****************************************************************************/ void FreeGalley(OBJECT hd, OBJECT stop_link, OBJECT *inners, OBJECT relocate_link, OBJECT sym) { OBJECT link, y, z, zlink, srch, index; assert( type(hd) == HEAD && sized(hd), "FreeGalley: pre!"); assert( Up(hd) != hd, "FreeGalley: Up(hd)!" ); assert( *inners == nilobj || type(*inners) == ACAT, "FreeGalley: ACAT!" ); debug3(DGA, D, "[ FreeGalley(Galley %s into %s); rl %s nilobj", SymName(actual(hd)), SymName(whereto(hd)), relocate_link == nilobj ? "==" : "!="); /* close targets and move or flush any inner galleys */ for( link = Down(hd); link != stop_link; link = NextDown(link) ) { Child(y, link); if( type(y) == RECEIVING && actual(actual(y)) == InputSym ) Error(22, 5, "forcing galley after input point", WARN, &fpos(actual(y))); else if( type(y) == RECEIVING ) { /* either relocate or free each galley */ for( zlink = Down(y); zlink != y; ) { Child(z, zlink); zlink = NextDown(zlink); assert( type(z) == HEAD, "FreeGalley/RECEIVING: type(z) != HEAD!" ); debug1(DGA, D, "FreeGalley examining galley %s", SymName(actual(z))); if( relocate_link != nilobj && whereto(z) != sym && (srch = SearchGalley(relocate_link, whereto(z), TRUE, FALSE, TRUE, FALSE)) != nilobj ) { if( opt_components(z) != nilobj ) GazumpOptimize(z, actual(y)); debug2(DGA, D, " FreeGalley relocating %s to just before %s", SymName(actual(z)), SymName(whereto(z))); DetachGalley(z); Parent(index, Up(z)); MoveLink(Up(index), Up(srch), PARENT); /* just before new dest */ } else { debug1(DGA, D, " FreeGalley freeing galley %s", SymName(actual(z))); FreeGalley(z, z, inners, nilobj, sym); if( *inners == nilobj ) New(*inners, ACAT); Link(*inners, y); } } non_blocking(y) = TRUE; } else if( type(y) == RECEPTIVE ) { non_blocking(y) = TRUE; } } debug0(DGA, D, "] FreeGalley returning."); } /* end FreeGalley */ /*@::SetTarget()@*************************************************************/ /* */ /* SetTarget(hd) */ /* */ /* Search for the target of unsized galley hd, and set the following: */ /* */ /* whereto(hd) The symbol which is this galley's target */ /* foll_or_prec(hd) GALL_FOLL, GALL_PREC, GALL_FOLL_OR_PREC */ /* force_gall(hd) TRUE is this is a forcing galley */ /* */ /*****************************************************************************/ void SetTarget(OBJECT hd) { OBJECT x, y, link, cr, lpar, rpar, env; BOOLEAN copied; debug1(DGS, DD, "SetTarget(%s)", SymName(actual(hd))); assert( type(hd) == HEAD, "SetTarget: type(hd) != HEAD!" ); Child(x, Down(hd)); assert( type(x) == CLOSURE, "SetTarget: type(x) != CLOSURE!" ); assert( has_target(actual(x)), "SetTarget: x has no target!" ); /* search the parameters of x for @Target */ cr = nilobj; for( link = Down(x); link != x; link = NextDown(link) ) { Child(y, link); if( type(y) == PAR && is_target(actual(y)) ) { assert( Down(y) != y, "SetTarget: Down(PAR)!" ); Child(cr, Down(y)); break; } } /* search the children of actual(x) for a default value of @Target */ if( cr == nilobj ) for( link = Down(actual(x)); link != actual(x); link = NextDown(link) ) { Child(y, link); if( is_target(y) ) { cr = sym_body(y); break; } } assert(cr != nilobj, "SetTarget: cr == nilobj!"); /* if cr is not a cross-reference, expand it until it is */ copied = FALSE; if( !is_cross(type(cr)) ) { OBJECT nbt[2], nft[2], ntarget, ncrs, nenclose; nbt[COLM] = nft[COLM] = nbt[ROWM] = nft[ROWM] = nilobj; ntarget = ncrs = nenclose = nilobj; cr = CopyObject(cr, &fpos(x)); copied = TRUE; env = GetEnv(x); cr = Manifest(cr, env, &InitialStyle, nbt, nft, &ntarget, &ncrs, FALSE, FALSE, &nenclose, TRUE); } /* check that cr is now a cross-reference object */ debug1(DGS, DD, "SetTarget examining %s", EchoObject(cr)); debug1(DGS, DD, " type(cr) = %s", Image( (int) type(cr)) ); if( !is_cross(type(cr)) ) Error(22, 6, "target of %s is not a cross reference", FATAL, &fpos(cr), SymName(actual(x))); /* determine which symbol is the target of this galley */ Child(lpar, Down(cr)); if( type(lpar) != CLOSURE ) Error(22, 7, "left parameter of %s is not a symbol", FATAL, &fpos(lpar), KW_CROSS); whereto(hd) = actual(lpar); /* determine the direction from the right parameter */ Child(rpar, NextDown(Down(cr))); if( !is_word(type(rpar)) ) { Error(22, 8, "replacing %s%s? by %s%s%s", WARN, &fpos(rpar), SymName(actual(lpar)), KW_CROSS, SymName(actual(lpar)), KW_CROSS, KW_FOLLOWING); foll_or_prec(hd) = GALL_FOLL; } else if( StringEqual(string(rpar), KW_PRECEDING) ) foll_or_prec(hd) = GALL_PREC; else if( StringEqual(string(rpar), KW_FOLLOWING) ) foll_or_prec(hd) = GALL_FOLL; else if( StringEqual(string(rpar), KW_FOLL_OR_PREC) ) foll_or_prec(hd) = GALL_FOLL_OR_PREC; else { Error(22, 9, "replacing %s%s%s by %s%s%s", WARN, &fpos(rpar), SymName(actual(lpar)), KW_CROSS, string(rpar), SymName(actual(lpar)), KW_CROSS, KW_FOLLOWING); foll_or_prec(hd) = GALL_FOLL; } /* determine whether this is a forcing galley */ force_gall(hd) = force_target(actual(hd)) || type(cr) == FORCE_CROSS; if( copied ) DisposeObject(cr); } /* end SetTarget */ /*@::CheckComponentOrder()@***************************************************/ /* */ /* int CheckComponentOrder(preceder, follower) */ /* */ /* Check the ordering relation between components preceder and follower, */ /* and return its current status: */ /* */ /* CLEAR follower definitely follows preceder, and always will; */ /* PROMOTE follower is not prevented from following preceder; */ /* CLOSE follower must move down its galley to follow preceder; */ /* BLOCK follower cannot be guaranteed to follow preceder. */ /* */ /*****************************************************************************/ int CheckComponentOrder(OBJECT preceder, OBJECT follower) { OBJECT prec_galley, foll_galley, z; int res; debug2(DGS, DD, "CheckComponentOrder( %s, %s )", EchoObject(preceder), EchoObject(follower)); Parent(prec_galley, Up(preceder)); Parent(foll_galley, Up(follower)); if( prec_galley == foll_galley ) { res = CLOSE; for( z = Up(follower); z != foll_galley; z = pred(z, CHILD) ) if( z == Up(preceder) ) { res = CLEAR; break; } } else { res = PROMOTE; while( Up(prec_galley) != prec_galley ) { Parent(z, Up(prec_galley)); /* index of galley */ Parent(prec_galley, Up(z)); /* enclosing galley */ if( prec_galley == foll_galley ) { res = BLOCK; break; } } } debug1(DGS, DD, "CheckComponentOrder returning %s", Image(res)); return res; } /* end CheckComponentOrder */