aboutsummaryrefslogtreecommitdiffstats
path: root/z22.c
diff options
context:
space:
mode:
authorJeffrey H. Kingston <jeff@it.usyd.edu.au>2010-09-14 19:21:41 +0000
committerJeffrey H. Kingston <jeff@it.usyd.edu.au>2010-09-14 19:21:41 +0000
commit71bdb35d52747e6d7d9f55df4524d57c2966be94 (patch)
tree480ee5eefccc40d5f3331cc52d66f722fd19bfb9 /z22.c
parentb41263ea7578fa9742486135c762803b52794105 (diff)
downloadlout-71bdb35d52747e6d7d9f55df4524d57c2966be94.tar.gz
Lout 3.17.
git-svn-id: http://svn.savannah.nongnu.org/svn/lout/trunk@2 9365b830-b601-4143-9ba8-b4a8e2c3339c
Diffstat (limited to 'z22.c')
-rw-r--r--z22.c1070
1 files changed, 1070 insertions, 0 deletions
diff --git a/z22.c b/z22.c
new file mode 100644
index 0000000..cbb6801
--- /dev/null
+++ b/z22.c
@@ -0,0 +1,1070 @@
+/*@z22.c:Galley Service:Interpose()@******************************************/
+/* */
+/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.17) */
+/* COPYRIGHT (C) 1991, 1999 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: z22.c */
+/* MODULE: Galley Service */
+/* EXTERNS: Interpose(), FlushInners(), ExpandRecursives(), */
+/* Promote(), KillGalley(), FreeGalley(), */
+/* SetTarget(), CheckComponentOrder() */
+/* */
+/*****************************************************************************/
+#include "externs.h"
+
+
+/*****************************************************************************/
+/* */
+/* 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;
+
+ 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, ")\n");
+ )
+
+ /* 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(DGF, D, "] FlushInners returning (PrintSym)");
+ return;
+ }
+ }
+
+ while( Down(inners) != inners )
+ { Child(y, Down(inners));
+ DeleteLink(Down(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(DGF,D," possibly calling FlushGalley %s from FlushInners (a)",
+ SymName(actual(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(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, n1, inners, newrecs, hd, tmp, env, why;
+ debug0(DCR, DD, "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, DD, " 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;
+ 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;
+ 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);
+ debug0(DCR, DDD, " as galley:");
+ ifdebug(DCR, DDD, DebugObject(hd));
+ debug1(DGS, D, "[ ExpandRecursives calling Constrained(%s, COLM)",
+ EchoObject(target));
+ Constrained(target, &hc, COLM, &why);
+ debug2(DGS, D, "] ExpandRecursives Constrained(%s, COLM) = %s",
+ EchoObject(target), EchoConstraint(&hc));
+ debug3(DCR, DD, " 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, DD, " 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, DD, " 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, DD, " 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);
+ }
+ 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, DD, "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;
+ 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 CLOSURE:
+ case NULL_CLOS:
+ 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 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:
+
+ 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 */
+
+/*@::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. */
+/* */
+/*****************************************************************************/
+
+void Promote(OBJECT hd, OBJECT stop_link, OBJECT dest_index, BOOLEAN join_after)
+{
+ /* these two variables refer to the root galley only */
+ static BOOLEAN first = TRUE; /* TRUE when the first component not written */
+ static OBJECT page_label=nilobj; /* current page label object */
+
+ OBJECT dest, link, y, z, tmp1, tmp2, why; FULL_CHAR *label_string;
+ int dim;
+ debug1(DGS, DD, "Promote(%s, stop_link)", SymName(actual(hd)));
+
+ 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);
+ 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_language(last) = word_language(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 */
+ /* ***
+ New(z, NULL_CLOS);
+ FposCopy(fpos(z), fpos(y));
+ back(z, COLM) = 0;
+ fwd(z, COLM) = 0;
+ Link(opt_components(hd), z);
+ debug1(DOG, DD, " adding %s", KW_NULL);
+ *** */
+ }
+ 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 )
+ {
+ /* galley is preceding or foll_or_prec, send to CrossSequence */
+ OBJECT t;
+ type(y) = GALL_PREC;
+ pinpoint(y) = nilobj;
+ Child(t, Down(z));
+ actual(y) = CrossMake(whereto(z), t, GALL_PREC);
+ 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 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 ACAT:
+ case HCAT:
+ case ROW_THR:
+
+ case CLOSURE:
+ case NULL_CLOS:
+ 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);
+ FixAndPrintObject(y, back(y, COLM), back(y, COLM), fwd(y, COLM),
+ COLM, FALSE, 0, 0);
+
+ /* print prefatory or page separating material, including fonts */
+ label_string = page_label != nilobj && is_word(type(page_label)) ?
+ string(page_label) : AsciiToFull("?");
+ debug1(DGS, DD, "root promote definite; label_string = %s",
+ label_string);
+ debug1(DCR, D, "label_string = %s", label_string);
+ if( first )
+ { PrintBeforeFirst(size(hd, COLM), size(y, ROWM), label_string);
+ first = FALSE;
+ }
+ else PrintBetween(size(hd, COLM), size(y, ROWM), label_string);
+ if( page_label != nilobj )
+ { DisposeObject(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);
+
+ }
+ 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
+ { for( link = hd; NextDown(link) != stop_link; )
+ { Child(y, NextDown(link));
+ 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.");
+} /* 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 */