aboutsummaryrefslogtreecommitdiffstats
path: root/z08.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 /z08.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 'z08.c')
-rw-r--r--z08.c1989
1 files changed, 1989 insertions, 0 deletions
diff --git a/z08.c b/z08.c
new file mode 100644
index 0000000..89afcbe
--- /dev/null
+++ b/z08.c
@@ -0,0 +1,1989 @@
+/*@z08.c:Object Manifest:ReplaceWithSplit()@**********************************/
+/* */
+/* 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: z08.c */
+/* MODULE: Object Manifest */
+/* EXTERNS: Manifest() */
+/* */
+/*****************************************************************************/
+#include "externs.h"
+#define line_breaker(g) \
+ (vspace(g) > 0 || (units(gap(g)) == FRAME_UNIT && width(gap(g)) > FR))
+
+
+/*****************************************************************************/
+/* */
+/* static SetUnderline(x) */
+/* */
+/* Set underline() flags in object x to UNDER_ON as appropriate. */
+/* */
+/*****************************************************************************/
+
+static void SetUnderline(OBJECT x)
+{ OBJECT y, link;
+ debug2(DOM, DD, " Manifest underlining %s %s", Image(type(x)),EchoObject(x));
+ if( type(x) == ACAT )
+ { for( link = Down(x); link != x; link = NextDown(link) )
+ { Child(y, link);
+ SetUnderline(y);
+ }
+ }
+ debug3(DOM, DDD, " SetUnderline underline() := %s for %s %s",
+ "UNDER_ON", Image(type(x)), EchoObject(x));
+ underline(x) = UNDER_ON;
+} /* end SetUnderline */
+
+
+/*****************************************************************************/
+/* */
+/* static ReplaceWithSplit(x, bthr, fthr) */
+/* */
+/* Replace object x with a SPLIT object, if threads for this object are */
+/* requested by bthr and/or fthr. */
+/* */
+/*****************************************************************************/
+
+#define ReplaceWithSplit(x, bthr, fthr) \
+ if( bthr[ROWM] || bthr[COLM] || fthr[ROWM] || fthr[COLM] ) \
+ x = insert_split(x, bthr, fthr)
+
+static OBJECT insert_split(OBJECT x, OBJECT bthr[2], OBJECT fthr[2])
+{ OBJECT res, new_op; int dim;
+ debug1(DOM, DD, "ReplaceWithSplit(%s, -)", EchoObject(x));
+ assert( type(x) != SPLIT, "ReplaceWithSplit: type(x) already SPLIT!" );
+ New(res, SPLIT);
+ FposCopy(fpos(res), fpos(x));
+ ReplaceNode(res, x);
+ for( dim = COLM; dim <= ROWM; dim++ )
+ { if( bthr[dim] || fthr[dim] )
+ { New(new_op, dim == COLM ? COL_THR : ROW_THR);
+ thr_state(new_op) = NOTSIZED;
+ fwd(new_op, 1-dim) = 0; /* will hold max frame_size */
+ back(new_op, 1-dim) = 0; /* will hold max frame_origin */
+ FposCopy(fpos(new_op), fpos(x));
+ Link(res, new_op); Link(new_op, x);
+ if( bthr[dim] ) Link(bthr[dim], new_op);
+ if( fthr[dim] ) Link(fthr[dim], new_op);
+ }
+ else Link(res, x);
+ }
+
+ debug1(DOM, DD, "ReplaceWithSplit returning %s", EchoObject(res));
+ return res;
+} /* end insert_split */
+
+/*@::ReplaceWithTidy()@*******************************************************/
+/* */
+/* OBJECT ReplaceWithTidy(x, one_word) */
+/* */
+/* Replace object x with a tidier version in which juxtapositions are */
+/* folded. If this is not possible, return the original object. */
+/* */
+/* If one_word is TRUE, the result is to be a single QWORD with inter- */
+/* word spaces converted to single space characters. Otherwise an ACAT */
+/* is the preferred result. */
+/* */
+/* *** Meaning changed, now interword spaces are converted to the same */
+/* number of spaces to assist construction of sorting keys. */
+/* */
+/*****************************************************************************/
+
+OBJECT ReplaceWithTidy(OBJECT x, BOOLEAN one_word)
+{ static FULL_CHAR buff[MAX_BUFF]; /* the growing current word */
+ static int buff_len; /* length of current word */
+ static FILE_POS buff_pos; /* filepos of current word */
+ static unsigned buff_typ; /* WORD or QWORD of current */
+ OBJECT link, y, tmp, res; /* temporaries */
+ int i;
+ debug2(DOM, DD, "ReplaceWithTidy(%s, %s)", EchoObject(x), bool(one_word));
+ switch( type(x) )
+ {
+ case ACAT:
+
+ for( link = Down(x); link != x; link = NextDown(link) )
+ { Child(y, link);
+ if( type(y) == ACAT )
+ { tmp = Down(y); TransferLinks(tmp, y, link);
+ DisposeChild(link); link = PrevDown(tmp);
+ }
+ }
+ res = nilobj; buff_len = 0; buff_typ = WORD;
+ FposCopy(buff_pos, fpos(x));
+ for( link = Down(x); link != x; link = NextDown(link) )
+ { Child(y, link);
+ if( is_word(type(y)) )
+ { if( buff_len + StringLength(string(y)) >= MAX_BUFF )
+ Error(8, 1, "word is too long", WARN, &fpos(y));
+ else
+ { if( buff_len == 0 ) FposCopy(buff_pos, fpos(y));
+ StringCopy(&buff[buff_len], string(y));
+ buff_len += StringLength(string(y));
+ if( type(y) == QWORD ) buff_typ = QWORD;
+ }
+ }
+ else if( type(y) == GAP_OBJ )
+ { if( Down(y) != y || hspace(y) + vspace(y) > 0 )
+ { if( one_word )
+ { if( buff_len + hspace(y) + vspace(y) >= MAX_BUFF )
+ Error(8, 2, "word is too long", WARN, &fpos(y));
+ else
+ { for( i = 0; i < hspace(y) + vspace(y); i++ )
+ { StringCopy(&buff[buff_len], AsciiToFull(" "));
+ buff_len++;
+ }
+ buff_typ = QWORD;
+ }
+ }
+ else
+ { tmp = MakeWord(buff_typ, buff, &buff_pos);
+ buff_len = 0; buff_typ = WORD;
+ if( res == nilobj )
+ { New(res, ACAT);
+ FposCopy(fpos(res), fpos(x));
+ }
+ Link(res, tmp); Link(res, y);
+ }
+ }
+ }
+ else /* error */
+ { if( res != nilobj ) DisposeObject(res);
+ debug0(DOM, DD, "ReplaceWithTidy returning unchanged");
+ return x;
+ }
+ }
+ tmp = MakeWord(buff_typ, buff, &buff_pos);
+ if( res == nilobj ) res = tmp;
+ else Link(res, tmp);
+ ReplaceNode(res, x); DisposeObject(x);
+ debug1(DOM, DD, "ReplaceWithTidy returning %s", EchoObject(res));
+ return res;
+
+
+ case WORD:
+ case QWORD:
+
+ debug1(DOM, DD, "ReplaceWithTidy returning %s", EchoObject(x));
+ return x;
+
+
+ default:
+
+ debug0(DOM, DD, "ReplaceWithTidy returning unchanged");
+ return x;
+ }
+} /* end ReplaceWithTidy */
+
+
+/*@::GetScaleFactor()@********************************************************/
+/* */
+/* static float GetScaleFactor(x) */
+/* */
+/* Find a scale factor in object x and return it as a float, after checks. */
+/* */
+/*****************************************************************************/
+
+static float GetScaleFactor(OBJECT x)
+{ float scale_factor;
+ if( !is_word(type(x)) )
+ { Error(8, 3, "replacing invalid scale factor by 1.0", WARN, &fpos(x));
+ scale_factor = 1.0;
+ }
+ else if( sscanf( (char *) string(x), "%f", &scale_factor) != 1 )
+ { Error(8, 4, "replacing invalid scale factor %s by 1.0",
+ WARN, &fpos(x), string(x));
+ scale_factor = 1.0;
+ }
+ else if( scale_factor < 0.01 )
+ { Error(8, 5, "replacing undersized scale factor %s by 1.0",
+ WARN, &fpos(x), string(x));
+ scale_factor = 1.0;
+ }
+ else if( scale_factor > 100 )
+ { Error(8, 6, "replacing oversized scale factor %s by 1.0",
+ WARN, &fpos(x), string(x));
+ scale_factor = 1.0;
+ }
+ return scale_factor;
+} /* GetScaleFactor */
+
+
+static OBJECT nbt[2] = { nilobj, nilobj }; /* constant nilobj threads */
+static OBJECT nft[2] = { nilobj, nilobj }; /* constant nilobj threads */
+static OBJECT ntarget = nilobj; /* constant nilobj target */
+static OBJECT nenclose = nilobj; /* constant nilobj enclose */
+
+
+/*@::ManifestCat@*************************************************************/
+/* */
+/* OBJECT ManifestCat(x,env,style,bthr, fthr, target, crs, ok, need_expand, */
+/* enclose, fcr) */
+/* */
+/* This procedure implements Manifest (see below) when x is HCAT or VCAT. */
+/* */
+/*****************************************************************************/
+
+static OBJECT ManifestCat(OBJECT x, OBJECT env, STYLE *style, OBJECT bthr[2],
+OBJECT fthr[2], OBJECT *target, OBJECT *crs, BOOLEAN ok, BOOLEAN need_expand,
+OBJECT *enclose, BOOLEAN fcr)
+{ OBJECT bt[2], ft[2], y, link, gaplink, g, first_bt, last_ft, z;
+ int par, perp;
+ unsigned res_inc; BOOLEAN still_backing;
+ STYLE new_style;
+ debug1(DOM, DD, "[ ManifestCat(%s)", EchoObject(x));
+
+ StyleCopy(new_style, *style);
+ if( type(x) == HCAT )
+ { par = ROWM;
+ adjust_cat(x) = hadjust(*style);
+ hadjust(new_style) = FALSE;
+ }
+ else
+ { par = COLM;
+ adjust_cat(x) = vadjust(*style);
+ vadjust(new_style) = FALSE;
+ }
+ perp = 1 - par;
+ link = Down(x);
+ gaplink = NextDown(link);
+ assert( link!=x && gaplink!=x, "Manifest/VCAT: less than two children!" );
+ Child(y, link); Child(g, gaplink);
+
+ /* set bt and ft threads for y */
+ bt[perp] = bthr[perp];
+ ft[perp] = fthr[perp];
+ if( bthr[par] ) { New(first_bt, THREAD); }
+ else first_bt = nilobj;
+ bt[par] = first_bt;
+ if( join(gap(g)) ) { New(ft[par], THREAD); }
+ else ft[par] = nilobj;
+ still_backing = first_bt != nilobj;
+
+ /* manifest y and insinuate any cross-references */
+ y = Manifest(y, env, &new_style, bt, ft, target, crs, ok, FALSE, enclose, fcr);
+ if( type(x) == VCAT && ok && *crs != nilobj )
+ { debug1(DCR, DD, " insinuating %s", EchoObject(*crs));
+ TransferLinks(Down(*crs), *crs, link);
+ DisposeObject(*crs);
+ *crs = nilobj;
+ }
+
+ /* manifest the remaining children */
+ while( g != nilobj )
+ {
+ /* manifest the gap object, store it in gap(g), add perp threads */
+ assert( type(g) == GAP_OBJ, "Manifest/VCAT: type(g) != GAP_OBJECT!" );
+ assert( Down(g) != g, "Manifest/VCAT: GAP_OBJ has no child!" );
+ Child(z, Down(g));
+ debug1(DOM, DD, "manifesting gap, style = %s", EchoStyle(style));
+ z = Manifest(z, env, &new_style, nbt, nft, &ntarget, crs, FALSE, FALSE, enclose, fcr);
+ debug1(DOM, DD, "replacing with tidy, style = %s", EchoStyle(style));
+ z = ReplaceWithTidy(z, FALSE);
+ debug1(DOM, DD, "calling GetGap, style = %s", EchoStyle(style));
+ GetGap(z, style, &gap(g), &res_inc);
+ if( bt[perp] ) Link(bt[perp], g);
+ if( ft[perp] ) Link(ft[perp], g);
+
+ /* find the next child y, and following gap if any */
+ link = NextDown(gaplink);
+ assert( link != x, "Manifest/VCAT: GAP_OBJ is last child!" );
+ Child(y, link);
+ gaplink = NextDown(link);
+ if( gaplink == x ) g = nilobj;
+ else Child(g, gaplink);
+
+ /* set bt and ft threads for y */
+ last_ft = ft[par];
+ if( ft[par] ) { New(bt[par], THREAD); } else bt[par] = nilobj;
+ if( g != nilobj )
+ { if( join(gap(g)) ) { New(ft[par], THREAD); } else ft[par] = nilobj;
+ }
+ else
+ {
+ if( fthr[par] ) { New(ft[par], THREAD); } else ft[par] = nilobj;
+ }
+
+ /* manifest y and insinuate any cross references */
+ y = Manifest(y, env, &new_style, bt, ft, target, crs, ok, FALSE, enclose, fcr);
+ if( type(x) == VCAT && ok && *crs != nilobj )
+ { debug1(DCR, DD, " insinuating %s", EchoObject(*crs));
+ TransferLinks(Down(*crs), *crs, link);
+ DisposeObject(*crs);
+ *crs = nilobj;
+ }
+
+ if( bt[par] ) /* then thread lists last_ft and bt[par] must merge */
+ { OBJECT llink, rlink, lthread, rthread;
+ BOOLEAN goes_through;
+ assert( Down(bt[par]) != bt[par], "Manifest: bt[par] no children!" );
+ assert( last_ft!=nilobj && Down(last_ft)!=last_ft, "Manifest:last_ft!" );
+
+ /* check whether marks run right through y in par direction */
+ goes_through = FALSE;
+ if( ft[par] )
+ { assert( Down(ft[par]) != ft[par], "Manifest: ft[par] child!" );
+ Child(lthread, LastDown(bt[par]));
+ Child(rthread, LastDown(ft[par]));
+ goes_through = lthread == rthread;
+ }
+
+ /* merge the thread lists */
+ llink = Down(last_ft); rlink = Down(bt[par]);
+ while( llink != last_ft && rlink != bt[par] )
+ { Child(lthread, llink);
+ Child(rthread, rlink);
+ assert( lthread != rthread, "Manifest: lthread == rthread!" );
+ MergeNode(lthread, rthread);
+ llink = NextDown(llink);
+ rlink = NextDown(rlink);
+ }
+
+ /* attach leftover back threads to first_bt if required */
+ if( rlink != bt[par] )
+ {
+ if( still_backing ) TransferLinks(rlink, bt[par], first_bt);
+ }
+ DisposeObject(bt[par]);
+
+ /* attach leftover forward threads to ft[par] if required */
+ if( llink != last_ft )
+ {
+ if( goes_through ) TransferLinks(llink, last_ft, ft[par]);
+ }
+ DisposeObject(last_ft);
+
+ if( !goes_through ) still_backing = FALSE;
+
+ }
+ else still_backing = FALSE;
+
+ } /* end while */
+
+ /* export par threads */
+ if( fthr[par] ) MergeNode(fthr[par], ft[par]);
+ if( bthr[par] ) MergeNode(bthr[par], first_bt);
+ debug0(DOM, DD, "] ManifestCat returning");
+ return x;
+} /* end ManifestCat */
+
+
+/*@::ManifestCase@************************************************************/
+/* */
+/* OBJECT ManifestCase(x,env,style,bthr,fthr, target, crs, ok, need_expand, */
+/* enclose, fcr) */
+/* */
+/* This procedure implements Manifest (see below) when x is CASE. */
+/* */
+/*****************************************************************************/
+
+static OBJECT ManifestCase(OBJECT x, OBJECT env, STYLE *style, OBJECT bthr[2],
+OBJECT fthr[2], OBJECT *target, OBJECT *crs, BOOLEAN ok, BOOLEAN need_expand,
+OBJECT *enclose, BOOLEAN fcr)
+{ OBJECT y, tag, ylink, yield, ytag, zlink;
+ OBJECT res, z, firsttag, firstres;
+
+ /* make sure left parameter (the tag) is in order */
+ debug0(DOM, DD, " manifesting CASE now");
+ Child(tag, Down(x));
+ debug1(DOM, DD, " manifesting CASE tag %s now", EchoObject(tag));
+ tag = Manifest(tag, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE,
+ &nenclose, fcr);
+ tag = ReplaceWithTidy(tag, FALSE);
+
+ /* make sure the right parameter is an ACAT */
+ Child(y, LastDown(x));
+ if( type(y) == YIELD )
+ { New(z, ACAT);
+ MoveLink(Up(y), z, PARENT);
+ Link(x, z);
+ y = z;
+ }
+ if( type(y) != ACAT )
+ { Error(8, 7, "%s deleted (right parameter is malformed)",
+ WARN, &fpos(y), KW_CASE);
+ y = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ ReplaceNode(y, x); DisposeObject(x);
+ x = Manifest(y, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ return x;
+ }
+
+ /* hunt through right parameter for res, the selected case */
+ res = nilobj; firsttag = nilobj;
+ for( ylink = Down(y); ylink != y && res == nilobj; ylink = NextDown(ylink) )
+ { Child(yield, ylink);
+ if( type(yield) == GAP_OBJ ) continue;
+ if( type(yield) != YIELD )
+ { Error(8, 8, "%s expected here", WARN, &fpos(yield), KW_YIELD);
+ break;
+ }
+ Child(ytag, Down(yield));
+ ytag = Manifest(ytag, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE,
+ &nenclose, fcr);
+ ytag = ReplaceWithTidy(ytag, FALSE);
+ if( is_word(type(ytag)) )
+ { if( firsttag == nilobj )
+ { firsttag = ytag;
+ Child(firstres, LastDown(yield));
+ }
+ if( (is_word(type(tag)) && StringEqual(string(ytag), string(tag))) ||
+ StringEqual(string(ytag), STR_ELSE) )
+ { Child(res, LastDown(yield));
+ break;
+ }
+ }
+ else if( type(ytag) == ACAT )
+ { z = ytag;
+ for( zlink = Down(z); zlink != z; zlink = NextDown(zlink) )
+ { Child(ytag, zlink);
+ if( type(ytag) == GAP_OBJ ) continue;
+ if( !is_word(type(ytag)) )
+ { Error(8, 9, "error in left parameter of %s",
+ WARN, &fpos(ytag), KW_YIELD);
+ break;
+ }
+ if( firsttag == nilobj )
+ { firsttag = ytag;
+ Child(firstres, LastDown(yield));
+ }
+ if( (is_word(type(tag)) && StringEqual(string(ytag), string(tag)))
+ || StringEqual(string(ytag), STR_ELSE) )
+ { Child(res, LastDown(yield));
+ break;
+ }
+ }
+ }
+ else Error(8, 10, "error in left parameter of %s",
+ WARN, &fpos(ytag), KW_YIELD);
+ }
+ if( res == nilobj )
+ { if( firsttag != nilobj )
+ { Error(8, 11, "replacing unknown %s option %s by %s",
+ WARN, &fpos(tag), KW_CASE, string(tag), string(firsttag));
+ res = firstres;
+ debug1(DGP, D, " res = %s", EchoObject(res));
+ }
+ else
+ { Error(8, 12, "%s deleted (choice %s unknown)",
+ WARN, &fpos(tag), KW_CASE, string(tag));
+ y = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ ReplaceNode(y, x); DisposeObject(x);
+ x = Manifest(y, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ return x;
+ }
+ }
+
+ /* now manifest the result and replace x with it */
+ DeleteLink(Up(res));
+ ReplaceNode(res, x);
+ DisposeObject(x);
+ x = Manifest(res, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ return x;
+} /* ManifestCase */
+
+
+/*@::ManifestTg@**************************************************************/
+/* */
+/* OBJECT ManifestTg(x,env,style,bthr, fthr, target, crs, ok, need_expand, */
+/* enclose, fcr) */
+/* */
+/* This procedure implements Manifest (see below) when x is TAGGED. */
+/* */
+/*****************************************************************************/
+
+static OBJECT ManifestTg(OBJECT x, OBJECT env, STYLE *style, OBJECT bthr[2],
+OBJECT fthr[2], OBJECT *target, OBJECT *crs, BOOLEAN ok, BOOLEAN need_expand,
+OBJECT *enclose, BOOLEAN fcr)
+{ OBJECT y, tag, z;
+
+ /* make sure first argument is a cross-reference */
+ assert( Down(x) != x && NextDown(Down(x)) != x &&
+ NextDown(NextDown(Down(x))) == x, "Manifest TAGGED: children!" );
+ Child(y, Down(x));
+ if( !is_cross(type(y)) )
+ {
+ y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, TRUE);
+ if( !is_cross(type(y)) )
+ { Error(8, 13, "left parameter of %s is not a cross reference",
+ WARN, &fpos(y), KW_TAGGED);
+ y = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ ReplaceNode(y, x); DisposeObject(x);
+ x = Manifest(y, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ return x;
+ }
+ }
+
+ /* make sure the arguments of the cross-reference are OK */
+ Child(z, Down(y));
+ if( type(z) != CLOSURE )
+ { Error(8, 14, "left parameter of %s must be a symbol",
+ WARN, &fpos(y), KW_TAGGED);
+ y = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ ReplaceNode(y, x); DisposeObject(x);
+ x = Manifest(y, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ return x;
+ }
+ if( !has_tag(actual(z)) )
+ { Error(8, 15, "symbol %s not allowed here (it has no %s)",
+ WARN, &fpos(z), SymName(actual(z)), KW_TAG);
+ y = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ ReplaceNode(y, x); DisposeObject(x);
+ x = Manifest(y, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ return x;
+ }
+ Child(z, NextDown(Down(y)));
+ z = Manifest(z, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ z = ReplaceWithTidy(z, FALSE);
+ if( is_word(type(z)) && StringEqual(string(z), KW_PRECEDING) )
+ cross_type(y) = CROSS_PREC;
+ else if( is_word(type(z)) && StringEqual(string(z), KW_FOLLOWING) )
+ cross_type(y) = CROSS_FOLL;
+ else if( is_word(type(z)) && StringEqual(string(z), KW_FOLL_OR_PREC) )
+ cross_type(y) = CROSS_FOLL_OR_PREC;
+ else
+ { Error(8, 16, "%s, %s or %s expected in left parameter of %s",
+ WARN, &fpos(z), KW_PRECEDING, KW_FOLLOWING, KW_FOLL_OR_PREC, KW_TAGGED);
+ y = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ ReplaceNode(y, x); DisposeObject(x);
+ x = Manifest(y, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ return x;
+ }
+
+ /* make sure second argument (the new key) is ok */
+ Child(tag, LastDown(x));
+ tag = Manifest(tag, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ tag = ReplaceWithTidy(tag, TRUE); /* && */
+ if( !is_word(type(tag)) )
+ { Error(8, 17, "right parameter of %s must be a simple word",
+ WARN, &fpos(tag), KW_TAGGED);
+ ifdebug(DOM, DD, DebugObject(tag));
+ y = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ ReplaceNode(y, x); DisposeObject(x);
+ x = Manifest(y, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ return x;
+ }
+
+ /* assemble insinuated cross reference which replaces x */
+ ReplaceNode(tag, z);
+ DisposeObject(z);
+ ReplaceNode(y, x);
+ DisposeObject(x);
+ x = y;
+ ReplaceWithSplit(x, bthr, fthr);
+ debug1(DCR, DD, " tagged manifesting %s", EchoObject(x));
+ return x;
+} /* end ManifestTg */
+
+
+/*@::ManifestCl@**************************************************************/
+/* */
+/* OBJECT ManifestCl(x,env,style,bthr, fthr, target, crs, ok, need_expand, */
+/* enclose, fcr) */
+/* */
+/* This procedure implements Manifest (see below) when x is CLOSURE. */
+/* */
+/*****************************************************************************/
+
+static OBJECT ManifestCl(OBJECT x, OBJECT env, STYLE *style, OBJECT bthr[2],
+OBJECT fthr[2], OBJECT *target, OBJECT *crs, BOOLEAN ok, BOOLEAN need_expand,
+OBJECT *enclose, BOOLEAN fcr)
+{ OBJECT y, link, sym, res_env, hold_env, hold_env2, z, newz, command;
+ BOOLEAN symbol_free;
+
+ sym = actual(x);
+ StyleCopy(save_style(x), *style);
+ debugcond2(DOM, D, StringEqual(SymName(sym), "@Section"),
+ "manifesting %s at %s", SymName(sym), EchoFilePos(&fpos(x)));
+ debug1(DOM, DD, " [ manifesting closure %s", SymName(sym));
+
+ /* enclose, if required */
+ if( *enclose != nilobj && (actual(x)==GalleySym || actual(x)==ForceGalleySym) )
+ { OBJECT sym, par;
+ ReplaceNode(*enclose, x);
+ Child(sym, Down(*enclose));
+ Child(par, Down(sym));
+ DisposeChild(Down(par));
+ Link(par, x);
+ x = *enclose;
+ *enclose = nilobj;
+ debug1(DHY, DD, " Manifest/enclose: %s", EchoObject(x));
+ x = Manifest(x, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ debug0(DOM, DD, " ] returning from manifesting closure (enclose)");
+ return x;
+ }
+
+ /* expand parameters where possible, and find if they are all free */
+ symbol_free = TRUE;
+ debugcond1(DOM, DD, indefinite(sym), " freeing %s", EchoObject(x));
+ for( link = Down(x); link != x; link = NextDown(link) )
+ { Child(y, link);
+ assert( type(y) == PAR, "Manifest/CLOSURE: type(y) != PAR!" );
+ Child(z, Down(y));
+
+ /* try to evaluate the actual parameter z */
+ if( !is_word(type(z)) && !has_par(actual(y)) )
+ {
+ if( is_tag(actual(y)) || is_key(actual(y)) )
+ { z = Manifest(z, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ z = ReplaceWithTidy(z, TRUE);
+ if( !is_word(type(z)) )
+ {
+ debug2(ANY, D, "z = %s %s", Image(type(z)), EchoObject(z));
+ Error(8, 41, "this %s is not a sequence of one or more words", FATAL,
+ &fpos(y), SymName(actual(y)));
+ }
+ }
+ else if( type(z) == NEXT )
+ { z = Manifest(z, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ z = ReplaceWithTidy(z, FALSE);
+ }
+ else if( type(z) == CLOSURE && is_par(type(actual(z))) )
+ {
+ /* see whether z would come to something simple if expanded */
+ newz = ParameterCheck(z, env);
+ debugcond2(DOM, DD, indefinite(sym), " ParameterCheck(%s, env) = %s",
+ EchoObject(z), EchoObject(newz));
+ if( newz != nilobj )
+ { ReplaceNode(newz, z);
+ DisposeObject(z);
+ z = newz;
+ }
+ }
+ }
+
+ /* now check z to see whether it is either a word or and ACAT of words */
+ /* ***
+ if( type(z) == ACAT )
+ { int i = 0; OBJECT t, tlink, g;
+ tlink = Down(z);
+ for( ; tlink != z && symbol_free; tlink = NextDown(tlink), i++ )
+ { Child(t, tlink);
+ switch( type(t) )
+ {
+ case WORD:
+ case QWORD: if( i > 20 ) symbol_free = FALSE;
+ break;
+
+ case GAP_OBJ: if( Down(t) != t )
+ { Child(g, Down(t));
+ if( !is_word(type(g)) ) symbol_free = FALSE;
+ }
+ break;
+
+ default: symbol_free = FALSE;
+ break;
+
+ }
+ }
+ }
+ else
+ *** */
+
+ if( !is_word(type(z)) )
+ { symbol_free = FALSE;
+ }
+ }
+ debugcond2(DOM, DD, indefinite(sym)," s_f = %s, x = %s",
+ bool(symbol_free), EchoObject(x));
+
+ /* if all parameters are free of symbols, optimize environment */
+ if( symbol_free && imports(sym) == nilobj && enclosing(sym) != StartSym )
+ { y = SearchEnv(env, enclosing(sym));
+ if( y != nilobj && type(y) == CLOSURE )
+ { OBJECT prntenv;
+ Parent(prntenv, Up(y));
+ if( type(prntenv) != ENV ) fprintf(stderr, "%s\n", Image(type(prntenv)));
+ assert(type(prntenv) == ENV, "Manifest: prntenv!");
+ if( Down(prntenv) == LastDown(prntenv) )
+ { env = prntenv;
+ }
+ else
+ { debug0(DCR, DDD, "calling SetEnv from Manifest (a)");
+ env = SetEnv(y, nilobj);
+ }
+ New(hold_env2, ACAT); Link(hold_env2, env);
+ }
+ else
+ { /* *** letting this through now
+ if( has_par(enclosing(sym)) )
+ Error(8, 18, "symbol %s used outside %s", WARN, &fpos(x), SymName(sym),
+ SymName(enclosing(sym)));
+ *** */
+ hold_env2 = nilobj;
+ }
+ }
+ else hold_env2 = nilobj;
+
+ debug3(DOM, DD, " expansion: has_target %s, indefinite %s, recursive %s",
+ bool(has_target(sym)), bool(indefinite(sym)), bool(recursive(sym)));
+ if( has_target(sym) && !need_expand )
+ {
+ /* convert symbols with targets to unsized galleys */
+ OBJECT hd;
+ New(hd, HEAD);
+ FposCopy(fpos(hd), fpos(x));
+ actual(hd) = sym;
+ limiter(hd) = opt_components(hd) = opt_constraints(hd) = nilobj;
+ gall_dir(hd) = horiz_galley(sym);
+ ready_galls(hd) = nilobj;
+ must_expand(hd) = TRUE;
+ sized(hd) = FALSE;
+ ReplaceNode(hd, x);
+ Link(hd, x);
+ AttachEnv(env, x);
+ SetTarget(hd);
+ enclose_obj(hd) = (has_enclose(sym) ? BuildEnclose(hd) : nilobj);
+ x = hd;
+ threaded(x) = bthr[COLM] != nilobj || fthr[COLM] != nilobj;
+ ReplaceWithSplit(x, bthr, fthr);
+ }
+ else if(
+ *target == sym ? (*target = nilobj, TRUE) :
+ need_expand ? TRUE :
+ uses_galley(sym) && !recursive(sym) ? TRUE :
+ !indefinite(sym) && !recursive(sym) ? TRUE :
+ indefinite(sym) && *target != nilobj ? SearchUses(sym, *target)
+ : FALSE
+ )
+ {
+ /* expand the closure and manifest the result */
+ debug1(DOM, DD, "expanding; style: %s", EchoStyle(style));
+ debug0(DCE, DD, " calling ClosureExpand from Manifest/CLOSURE");
+ /* *** now requesting cross refs always, not only if ok
+ x = ClosureExpand(x, env, ok, crs, &res_env);
+ *** */
+ x = ClosureExpand(x, env, TRUE, crs, &res_env);
+ New(hold_env, ACAT); Link(hold_env, res_env);
+ debug1(DOM, DD, "recursive call; style: %s", EchoStyle(style));
+ if( type(x) == FILTERED )
+ { assert( type(sym) == RPAR, "ManifestCl/filtered: type(sym)!" );
+ assert( filter(enclosing(sym)) != nilobj, "ManifestCl filter-encl!" );
+ New(command, CLOSURE);
+ FposCopy(fpos(command), fpos(x));
+ actual(command) = filter(enclosing(sym));
+ FilterSetFileNames(x);
+ command = Manifest(command,env,style,nbt,nft,&ntarget,crs,FALSE,FALSE, &nenclose, fcr);
+ command = ReplaceWithTidy(command, TRUE);
+ if( !is_word(type(command)) )
+ Error(8, 19, "filter parameter of %s symbol is not simple",
+ FATAL, &fpos(command), SymName(enclosing(sym)));
+ y = FilterExecute(x, string(command), res_env);
+ DisposeObject(command);
+ ReplaceNode(y, x);
+ DisposeObject(x);
+ x = y;
+ }
+ x = Manifest(x, res_env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ DisposeObject(hold_env);
+ }
+ else
+ {
+ AttachEnv(env, x);
+ threaded(x) = bthr[COLM] != nilobj || fthr[COLM] != nilobj;
+ debug0(DOM, DD, " closure; calling ReplaceWithSplit");
+ ReplaceWithSplit(x, bthr, fthr);
+ }
+ if( hold_env2 != nilobj ) DisposeObject(hold_env2);
+ debug0(DOM, DD, " ] returning from manifesting closure");
+ return x;
+} /* end ManifestCl */
+
+
+/*@::Manifest()@**************************************************************/
+/* */
+/* OBJECT Manifest(x, env, style, bthr, fthr, target, crs, ok, need_expand, */
+/* enclose, fcr) */
+/* */
+/* Manifest object x, interpreted in environment env and style style. */
+/* The result replaces x, and is returned also. */
+/* The manifesting operation converts x from a pure parse tree object */
+/* containing closures and no threads, to an object ready for sizing, */
+/* with fonts propagated to the words, fill styles propagated to the */
+/* ACATs, and line spacings propagated to all interested parties. */
+/* All non-recursive, non-indefinite closures are expanded. */
+/* Threads joining objects on a mark are constructed, and SPLIT objects */
+/* inserted, so that sizing becomes a trivial operation. */
+/* */
+/* Manifest will construct threads and pass them up as children of bthr[] */
+/* and fthr[] whenever non-nilobj values of these variables are passed in: */
+/* */
+/* bthr[COLM] protrudes upwards from x */
+/* fthr[COLM] protrudes downwards from x */
+/* bthr[ROWM] protrudes leftwards from x */
+/* fthr[ROWM] protrudes rightwards from x */
+/* */
+/* If *target != nilobj, Manifest will expand indefinite closures leading */
+/* to the first @Galley lying within an object of type *target. */
+/* */
+/* If *target != nilobj and *enclose != nilobj, Manifest will enclose */
+/* any @Galley or @ForceGalley it comes across in *enclose. */
+/* */
+/* The env parameter contains the environment in which x is to be */
+/* evaluated. Environments are shared, so their correct disposal is not */
+/* simple. The rule is this: the code which creates an environment, or */
+/* detaches it, is responsible for holding it with a dummy parent until */
+/* it is no longer required. */
+/* */
+/* Some objects x are not "real" in the sense that they do not give rise */
+/* to rectangles in the final printed document. The left parameter of */
+/* @Wide and similar operators, and the gap following a concatenation */
+/* operator, are examples of such non-real objects. The ok flag is true */
+/* when x is part of a real object. This is needed because some things, */
+/* such as the insinuation of cross references, the breaking of */
+/* lines @Break ACAT objects, and conversion to small capitals, only apply */
+/* to real objects. */
+/* */
+/* If *crs != nilobj, it points to a list of indexes to cross-references */
+/* which are to be insinuated into the manifested form of x if x is real. */
+/* */
+/* If need_expand is TRUE it forces closure x to expand. */
+/* */
+/* If fcr is TRUE, the objective is to expand until a cross-reference is */
+/* the result; so expansion will stop at a CROSS or FORCE_CROSS object. */
+/* */
+/* A postcondition of Manifest() is that the underline() flag is set to */
+/* either UNDER_ON or UNDER_OFF in every WORD, every QWORD, and every child */
+/* of every ACAT, including the gaps. This can be verified by checking */
+/* that the WORD and QWORD cases set underline() to UNDER_OFF, and the ACAT */
+/* case sets every child of the ACAT to UNDER_OFF. To see that the correct */
+/* subset of these flags gets changed to UNDER_ON, consult SetUnderline(). */
+/* The underline() flag is undefined otherwise, and should have value */
+/* UNDER_UNDEF. */
+/* */
+/*****************************************************************************/
+#define MAX_DEPTH 500
+
+OBJECT Manifest(OBJECT x, OBJECT env, STYLE *style, OBJECT bthr[2],
+OBJECT fthr[2], OBJECT *target, OBJECT *crs, BOOLEAN ok, BOOLEAN need_expand,
+OBJECT *enclose, BOOLEAN fcr)
+{ OBJECT bt[2], ft[2], y, link, gaplink, g; register FULL_CHAR *p;
+ OBJECT res, res_env, res_env2, hold_env, hold_env2, z, prev;
+ OBJECT link1, link2, x1, x2, y1, y2;
+ int par, num1, num2; GAP res_gap; unsigned res_inc; STYLE new_style;
+ BOOLEAN done, multiline; FULL_CHAR ch; float scale_factor;
+ static int depth = 0;
+#if DEBUG_ON
+ static unsigned int debug_type[MAX_DEPTH];
+ static OBJECT debug_actual[MAX_DEPTH];
+ static int debug_lnum[MAX_DEPTH];
+ BOOLEAN eee = (*enclose != nilobj);
+ debug_type[depth] = type(x);
+ debug_lnum[depth] = line_num(fpos(x));
+ if( type(x) == CLOSURE ) debug_actual[depth] = actual(x);
+ depth++;
+ if( depth == MAX_DEPTH )
+ { Error(8, 20, "maximum depth of symbol expansion (%d) reached",
+ WARN, &fpos(x), MAX_DEPTH);
+ Error(8, 21, "the symbols currently being expanded are:", WARN, &fpos(x));
+ while( --depth >= 0 )
+ {
+ Error(8, 22, "at %d: %d %s %s", WARN, &fpos(x), depth, debug_lnum[depth],
+ Image(debug_type[depth]), debug_type[depth] == CLOSURE ?
+ FullSymName(debug_actual[depth], AsciiToFull(".")) : STR_EMPTY);
+ }
+ Error(8, 23, "exiting now", FATAL, &fpos(x));
+ }
+#else
+ depth++;
+ if( depth == MAX_DEPTH )
+ {
+ Error(8, 40, "maximum depth of symbol expansion (%d) reached",
+ FATAL, &fpos(x), MAX_DEPTH);
+ }
+#endif
+
+ debug2(DOM, DD, "[Manifest(%s %s )", Image(type(x)), EchoObject(x));
+ debug1(DOM, DD, " environment: %s", EchoObject(env));
+ debug6(DOM, DD, " style: %s; target: %s; threads: %s%s%s%s",
+ EchoStyle(style), SymName(*target),
+ bthr[COLM] ? " up" : "", fthr[COLM] ? " down" : "",
+ bthr[ROWM] ? " left" : "", fthr[ROWM] ? " right" : "");
+ debugcond2(DHY, DD, eee, "[ Manifest(%s, *enclose = %s)",
+ EchoObject(x), EchoObject(*enclose));
+
+ switch( type(x) )
+ {
+
+ case ENV_OBJ:
+
+ debug0(DHY, DD, "[Manifest env_obj:");
+ ifdebug(DHY, DD, DebugObject(x));
+ Child(y, Down(x));
+ Child(res_env, NextDown(Down(x)));
+ assert( type(res_env) == ENV, "Manifest/ENV_OBJ: res_env!");
+ y = Manifest(y, res_env, style, bthr, fthr, target, crs, ok, TRUE, enclose, fcr);
+ /* we always expand children of ENV_OBJ (need_expand == TRUE) */
+ ReplaceNode(y, x);
+ DisposeObject(x);
+ x = y;
+ debug1(DHY, DD, "]Manifest env_obj returning %s", EchoObject(x));
+ break;
+
+
+ case CLOSURE:
+
+ x = ManifestCl(x, env, style, bthr, fthr, target, crs, ok, need_expand, enclose, fcr);
+ break;
+
+
+ case PAGE_LABEL:
+
+ Child(y, Down(x));
+ y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ y = ReplaceWithTidy(y, TRUE);
+ ReplaceWithSplit(x, bthr, fthr);
+ break;
+
+
+ case NULL_CLOS:
+
+ StyleCopy(save_style(x), *style);
+ ReplaceWithSplit(x, bthr, fthr);
+ break;
+
+
+ case CROSS:
+ case FORCE_CROSS:
+
+ assert( Down(x) != x && LastDown(x) != Down(x), "Manifest: CROSS child!");
+ if( !fcr ) /* stop if fcr, i.e. if purpose was to find a cross-reference */
+ {
+ debug0(DCR, DD, " calling CrossExpand from Manifest/CROSS");
+ Child(y, Down(x));
+ if( type(y) == CLOSURE )
+ {
+ /* *** want cross ref now always, not only if ok
+ x = CrossExpand(x, env, style, ok, crs, &res_env);
+ *** */
+ x = CrossExpand(x, env, style, crs, &res_env);
+ assert( type(x) == CLOSURE, "Manifest/CROSS: type(x)!" );
+ New(hold_env, ACAT); Link(hold_env, res_env);
+ /* expand here (calling Manifest immediately makes unwanted cr) */
+ debug0(DCE, DD, " calling ClosureExpand from Manifest/CROSS");
+ x = ClosureExpand(x, res_env, FALSE, crs, &res_env2);
+ New(hold_env2, ACAT); Link(hold_env2, res_env2);
+ x = Manifest(x, res_env2, style, bthr, fthr, target, crs, ok, TRUE, enclose, fcr);
+ DisposeObject(hold_env);
+ DisposeObject(hold_env2);
+ }
+ else
+ { y = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ ReplaceNode(y, x);
+ DisposeObject(x);
+ x = Manifest(y, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ }
+ }
+ break;
+
+
+ case WORD:
+ case QWORD:
+
+ if( !ok || *crs == nilobj )
+ { word_font(x) = font(*style);
+ word_colour(x) = colour(*style);
+ word_language(x) = language(*style);
+ word_hyph(x) = hyph_style(*style) == HYPH_ON;
+ debug3(DOM, DDD, " manfifest/WORD underline() := %s for %s %s",
+ "UNDER_OFF", Image(type(x)), EchoObject(x));
+ if( small_caps(*style) && ok ) x = MapSmallCaps(x, style);
+ underline(x) = UNDER_OFF;
+ ReplaceWithSplit(x, bthr, fthr);
+ break;
+ }
+ New(y, ACAT);
+ FposCopy(fpos(y), fpos(x));
+ ReplaceNode(y, x);
+ Link(y, x); x = y;
+ /* NB NO BREAK! */
+
+
+ case ACAT:
+
+ StyleCopy(save_style(x), *style);
+ adjust_cat(x) = padjust(*style);
+ StyleCopy(new_style, *style);
+ padjust(new_style) = FALSE;
+ assert(Down(x) != x, "Manifest: ACAT!" );
+ link = Down(x); Child(y, link);
+ assert( type(y) != GAP_OBJ, "Manifest ACAT: GAP_OBJ is first!" );
+ multiline = FALSE;
+
+ /* manifest first child and insert any cross references */
+ if( is_word(type(y)) )
+ { word_font(y) = font(*style);
+ word_colour(y) = colour(*style);
+ word_language(y) = language(*style);
+ word_hyph(y) = hyph_style(*style) == HYPH_ON;
+ if( small_caps(*style) && ok ) y = MapSmallCaps(y, style);
+ }
+ else y = Manifest(y, env, &new_style, nbt, nft, target, crs, ok, FALSE, enclose, fcr);
+ debug3(DOM, DDD, " manfifest/ACAT1 underline() := %s for %s %s",
+ "UNDER_OFF", Image(type(y)), EchoObject(y));
+ underline(y) = UNDER_OFF;
+ /* ??? if( is_word(type(y)) ) */
+ if( ok && *crs != nilobj )
+ {
+ debug1(DCR, DD, " insinuating %s", EchoObject(*crs));
+ TransferLinks(Down(*crs), *crs, link);
+ DisposeObject(*crs);
+ *crs = nilobj;
+ }
+ prev = y;
+
+ /* manifest subsequent gaps and children */
+ for( gaplink = Down(link); gaplink != x; gaplink = NextDown(link) )
+ {
+ Child(g, gaplink);
+ assert( type(g) == GAP_OBJ, "Manifest ACAT: no GAP_OBJ!" );
+ debug3(DOM, DDD, " manfifest/ACAT2 underline() := %s for %s %s",
+ "UNDER_OFF", Image(type(g)), EchoObject(g));
+ underline(g) = UNDER_OFF;
+ link = NextDown(gaplink);
+ assert( link != x, "Manifest ACAT: GAP_OBJ is last!" );
+ Child(y, link);
+ assert( type(y) != GAP_OBJ, "Manifest ACAT: double GAP_OBJ!" );
+
+ /* manifest the next child */
+ debug1(DOM, DD, " in ACAT (3), style = %s", EchoStyle(style));
+ if( is_word(type(y)) )
+ { word_font(y) = font(*style);
+ word_colour(y) = colour(*style);
+ word_language(y) = language(*style);
+ word_hyph(y) = hyph_style(*style) == HYPH_ON;
+ if( small_caps(*style) && ok ) y = MapSmallCaps(y, style);
+ }
+ else y = Manifest(y, env, &new_style, nbt, nft, target, crs, ok, FALSE, enclose, fcr);
+ debug3(DOM, DDD, " manifest/ACAT3 underline() := %s for %s %s",
+ "UNDER_OFF", Image(type(y)), EchoObject(y));
+ underline(y) = UNDER_OFF;
+
+ /* manifest the gap object */
+ if( Down(g) != g )
+ {
+ /* explicit & operator whose value is the child of g */
+ Child(z, Down(g));
+ z = Manifest(z, env, &new_style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ z = ReplaceWithTidy(z, FALSE);
+ GetGap(z, style, &gap(g), &res_inc);
+ vspace(g) = hspace(g) = 0;
+ }
+ else
+ {
+ /* implicit & operator */
+ GapCopy(gap(g), space_gap(*style));
+ switch( space_style(*style) )
+ {
+ case SPACE_LOUT:
+
+ /* usual Lout spacing, the number of white space characters */
+ width(gap(g)) = width(gap(g)) * (vspace(g) + hspace(g));
+ break;
+
+
+ case SPACE_COMPRESS:
+
+ /* either zero or one space */
+ if( vspace(g) + hspace(g) == 0 )
+ { width(gap(g)) = 0;
+ }
+ else
+ { /* else width is like one space, so OK as is */
+ }
+ break;
+
+
+ case SPACE_SEPARATE:
+
+ /* exactly one space always, so do nothing further */
+ break;
+
+
+ case SPACE_TROFF:
+
+ /* Lout spacing plus one extra space for sentence end at eoln */
+ width(gap(g)) = width(gap(g)) * (vspace(g) + hspace(g));
+ debugcond2(DLS, DD, vspace(g) > 0, " prev = %s %s",
+ Image(type(prev)), EchoObject(prev));
+ if( vspace(g) > 0 )
+ {
+ /* set z to the preceding object; may need to search ACATs! */
+ z = prev;
+ while( type(z) == ACAT
+ || type(z) == ONE_COL || type(z) == ONE_ROW
+ || type(z) == HCONTRACT || type(z) == VCONTRACT )
+ { Child(z, LastDown(z));
+ }
+
+ /* if preceding object is a word, check for end sentence */
+ if( is_word(type(z)) )
+ {
+ for( p = string(z); *p != '\0'; p++ );
+ debug4(DLS, DD, " prev = %s, last = %c, LSE = %s, LWES = %s",
+ EchoObject(z), *(p-1), bool(LanguageSentenceEnds[*(p-1)]),
+ bool(LanguageWordEndsSentence(z, FALSE)));
+ if( p != string(z) && LanguageSentenceEnds[*(p-1)]
+ && LanguageWordEndsSentence(z, FALSE) )
+ width(gap(g)) += width(space_gap(*style));
+ }
+ }
+ break;
+
+
+ case SPACE_TEX:
+
+ if( vspace(g) + hspace(g) == 0 )
+ {
+ /* zero spaces gives zero result, as for compress above */
+ width(gap(g)) = 0;
+ }
+ else
+ {
+ /* set z to the preceding object; may need to search ACATs! */
+ z = prev;
+ while( type(z) == ACAT
+ || type(z) == ONE_COL || type(z) == ONE_ROW
+ || type(z) == HCONTRACT || type(z) == VCONTRACT )
+ { Child(z, LastDown(z));
+ }
+
+ /* one extra space if preceding is word ending sentence */
+ if( is_word(type(z)) )
+ {
+ for( p = string(z); *p != '\0'; p++ );
+ debug4(DLS, DD, " prev = %s, last = %c, LSE = %s, LWES = %s",
+ EchoObject(z), *(p-1), bool(LanguageSentenceEnds[*(p-1)]),
+ bool(LanguageWordEndsSentence(z, TRUE)));
+ if( p != string(z) && LanguageSentenceEnds[*(p-1)]
+ && LanguageWordEndsSentence(z, TRUE) )
+ width(gap(g)) += width(space_gap(*style));
+ }
+ }
+ break;
+
+
+ default:
+
+ assert(FALSE, "Manifest: unexpected space_style!");
+ break;
+ }
+ nobreak(gap(g)) = (width(gap(g)) == 0);
+ if( line_breaker(g) && is_definite(type(y)) ) multiline = TRUE;
+ }
+ debug1(DOM, DD, " in ACAT, gap = %s", EchoLength(width(gap(g))));
+
+ /* compress adjacent juxtaposed words of equal font, etc. */
+ if( is_word(type(y)) && width(gap(g)) == 0 && nobreak(gap(g)) &&
+ vspace(g)+hspace(g)==0 &&
+ units(gap(g)) == FIXED_UNIT && mode(gap(g)) == EDGE_MODE &&
+ prev != nilobj && is_word(type(prev)) && !mark(gap(g)) &&
+ word_font(prev) == word_font(y) &&
+ word_colour(prev) == word_colour(y) &&
+ word_language(prev) == word_language(y) )
+ /* no need to compare underline() since both are false */
+ { unsigned typ;
+ assert( underline(prev) == UNDER_OFF, "Manifest/ACAT: underline(prev)!" );
+ assert( underline(y) == UNDER_OFF, "Manifest/ACAT: underline(y)!" );
+ if( StringLength(string(prev))+StringLength(string(y)) >= MAX_BUFF )
+ Error(8, 24, "word %s%s is too long",
+ FATAL, &fpos(prev), string(prev), string(y));
+ z = y;
+ typ = type(prev) == QWORD || type(y) == QWORD ? QWORD : WORD;
+ y = MakeWordTwo(typ, string(prev), string(y), &fpos(prev));
+ word_font(y) = word_font(prev);
+ word_colour(y) = word_colour(prev);
+ word_language(y) = word_language(prev);
+ word_hyph(y) = word_hyph(prev);
+ underline(y) = UNDER_OFF;
+ debug3(DOM, DDD, " manifest/ACAT4 underline() := %s for %s %s",
+ "UNDER_OFF", Image(type(y)), EchoObject(y));
+ MoveLink(link, y, CHILD);
+ DisposeObject(z);
+ DisposeChild(Up(prev));
+ DisposeChild(gaplink);
+ }
+ prev = y;
+
+ /* insinuate any cross-references */
+ if( ok && *crs != nilobj )
+ {
+ debug1(DCR, DD, " insinuating %s", EchoObject(*crs));
+ TransferLinks(Down(*crs), *crs, link);
+ DisposeObject(*crs);
+ *crs = nilobj;
+ }
+ }
+
+ /* implement FILL_OFF break option if required */
+ if( ok && multiline && fill_style(*style) == FILL_UNDEF )
+ Error(8, 25, "missing %s symbol or option", FATAL, &fpos(x), KW_BREAK);
+ if( ok && multiline && fill_style(*style) == FILL_OFF )
+ { OBJECT prev_acat, new_acat; BOOLEAN jn;
+
+ /* compress any ACAT children of ACAT x */
+ for( link = x; NextDown(link) != x; link = NextDown(link) )
+ { Child(y, NextDown(link));
+ if( type(y) == ACAT )
+ { TransferLinks(Down(y), y, NextDown(link));
+ DisposeChild(Up(y));
+ link = PrevDown(link);
+ }
+ }
+
+ /* do line breaks now */
+ prev_acat = x;
+ New(x, VCAT);
+ adjust_cat(x) = FALSE;
+ ReplaceNode(x, prev_acat);
+ Link(x, prev_acat);
+ FirstDefinite(prev_acat, link, y, jn);
+ if( link != prev_acat )
+ {
+ NextDefiniteWithGap(prev_acat, link, y, g, jn);
+ while( link != prev_acat )
+ {
+ if( mode(gap(g)) != NO_MODE && line_breaker(g) )
+ { OBJECT glink = PrevDown(Up(g));
+ debug2(DOM, DD, "lines gap just before definite %s at %s",
+ Image(type(y)), EchoFilePos(&fpos(y)));
+ MoveLink(NextDown(glink), x, PARENT);
+ GapCopy(gap(g), line_gap(*style));
+ width(gap(g)) *= find_max(1, vspace(g));
+ New(new_acat, ACAT);
+ adjust_cat(new_acat) = padjust(*style);
+ FposCopy(fpos(new_acat), fpos(g));
+ if( hspace(g) > 0 )
+ { z = MakeWord(WORD, STR_EMPTY, &fpos(g));
+ word_font(z) = font(*style);
+ word_colour(z) = colour(*style);
+ word_language(z) = language(*style);
+ word_hyph(z) = hyph_style(*style) == HYPH_ON;
+ underline(z) = UNDER_OFF;
+ Link(new_acat, z);
+ New(z, GAP_OBJ);
+ hspace(z) = hspace(g);
+ vspace(z) = 0;
+ underline(z) = UNDER_OFF;
+ GapCopy(gap(z), space_gap(*style));
+ width(gap(z)) *= hspace(z);
+ Link(new_acat, z);
+ }
+ TransferLinks(NextDown(glink), prev_acat, new_acat);
+ StyleCopy(save_style(new_acat), *style);
+ Link(x, new_acat);
+ prev_acat = new_acat;
+ glink = prev_acat;
+ }
+ NextDefiniteWithGap(prev_acat, link, y, g, jn);
+ }
+ }
+
+ /* remove any singleton ACAT objects under x, if they are VCATs */
+ for( link = Down(x); link != x; link = NextDown(link) )
+ { Child(y, link);
+ if( type(y) == ACAT && Down(y) == LastDown(y) )
+ { Child(z, Down(y));
+ if( type(z) == VCAT )
+ { MoveLink(link, z, CHILD);
+ DisposeObject(y);
+ }
+ }
+ }
+ }
+
+ ReplaceWithSplit(x, bthr, fthr);
+ break;
+
+
+ case HCAT:
+ case VCAT:
+
+ x = ManifestCat(x, env, style, bthr, fthr, target, crs, ok, need_expand,
+ enclose, fcr);
+ break;
+
+
+ case WIDE:
+ case HIGH:
+
+ Child(y, Down(x));
+ y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ y = ReplaceWithTidy(y, FALSE);
+ GetGap(y, style, &res_gap, &res_inc);
+ if( res_inc != GAP_ABS || mode(res_gap) != EDGE_MODE ||
+ units(res_gap) != FIXED_UNIT )
+ { Error(8, 26, "replacing invalid left parameter of %s by 2i",
+ WARN, &fpos(y), Image(type(x)) );
+ units(res_gap) = FIXED_UNIT;
+ width(res_gap) = 2*IN;
+ }
+ SetConstraint(constraint(x), MAX_FULL_LENGTH, width(res_gap), MAX_FULL_LENGTH);
+ DisposeChild(Down(x));
+ goto ETC; /* two cases down from here */
+
+
+ case HSHIFT:
+ case VSHIFT:
+
+ Child(y, Down(x));
+ y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ y = ReplaceWithTidy(y, FALSE);
+ GetGap(y, style, &shift_gap(x), &res_inc);
+ shift_type(x) = res_inc;
+ if( mode(shift_gap(x)) != EDGE_MODE ||
+ (units(shift_gap(x))!=FIXED_UNIT && units(shift_gap(x))!=NEXT_UNIT) )
+ { Error(8, 27, "replacing invalid left parameter of %s by +0i",
+ WARN, &fpos(y), Image(type(x)) );
+ shift_type(x) = GAP_INC;
+ units(shift_gap(x)) = FIXED_UNIT;
+ width(shift_gap(x)) = 0;
+ mode(shift_gap(x)) = EDGE_MODE;
+ }
+ DisposeChild(Down(x));
+ goto ETC; /* next case down from here */
+
+
+ case HCONTRACT:
+ case VCONTRACT:
+ case HLIMITED:
+ case VLIMITED:
+ case HEXPAND:
+ case VEXPAND:
+ case ONE_COL:
+ case ONE_ROW:
+
+ ETC:
+ par = (type(x)==ONE_COL || type(x)==HEXPAND || type(x) == HCONTRACT ||
+ type(x)==HLIMITED || type(x)==WIDE || type(x)==HSHIFT) ? COLM : ROWM;
+ Child(y, Down(x));
+
+ /* manifest the child, propagating perp threads and suppressing pars */
+ bt[par] = ft[par] = nilobj;
+ bt[1-par] = bthr[1-par]; ft[1-par] = fthr[1-par];
+ y = Manifest(y, env, style, bt, ft, target, crs, ok, FALSE, enclose, fcr);
+
+ /* replace with split object if par threads needed */
+ bt[par] = bthr[par]; ft[par] = fthr[par];
+ bt[1-par] = ft[1-par] = nilobj;
+ ReplaceWithSplit(x, bt, ft);
+ break;
+
+
+ case HSPAN:
+ case VSPAN:
+
+ ReplaceWithSplit(x, bthr, fthr);
+ break;
+
+
+ case ROTATE:
+
+ Child(y, Down(x));
+ y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ y = ReplaceWithTidy(y, FALSE);
+ GetGap(y, style, &res_gap, &res_inc);
+ if( res_inc != GAP_ABS || mode(res_gap) != EDGE_MODE ||
+ units(res_gap) != DEG_UNIT )
+ { Error(8, 28, "replacing invalid left parameter of %s by 0d",
+ WARN, &fpos(y), Image(type(x)) );
+ units(res_gap) = DEG_UNIT;
+ width(res_gap) = 0;
+ }
+ sparec(constraint(x)) = width(res_gap);
+ DisposeChild(Down(x));
+ Child(y, Down(x));
+ y = Manifest(y, env, style, nbt, nft, target, crs, ok, FALSE,enclose,fcr);
+ ReplaceWithSplit(x, bthr, fthr);
+ break;
+
+
+ case BACKGROUND:
+
+ Child(y, Down(x));
+ y = Manifest(y, env, style, nbt, nft, target, crs, ok, FALSE,enclose,fcr);
+ Child(y, LastDown(x));
+ y = Manifest(y, env, style, nbt, nft, target, crs, ok, FALSE,enclose,fcr);
+ ReplaceWithSplit(x, bthr, fthr);
+ break;
+
+
+ case START_HVSPAN:
+ case START_HSPAN:
+ case START_VSPAN:
+ case HSCALE:
+ case VSCALE:
+ case HCOVER:
+ case VCOVER:
+
+ Child(y, Down(x));
+ y = Manifest(y, env, style, nbt, nft, target, crs, ok, FALSE,enclose,fcr);
+ ReplaceWithSplit(x, bthr, fthr);
+ break;
+
+
+ case SCALE:
+
+ Child(y, Down(x));
+ y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ y = ReplaceWithTidy(y, FALSE);
+ if( is_word(type(y)) && StringEqual(string(y), STR_EMPTY) )
+ {
+ /* missing scale factor, meaning to be inserted automatically */
+ bc(constraint(x)) = fc(constraint(x)) = 0; /* work out later */
+ }
+ else if( type(y) != ACAT )
+ {
+ /* presumably one word, common factor for horizontal and vertical */
+ scale_factor = GetScaleFactor(y);
+ bc(constraint(x)) = fc(constraint(x)) = scale_factor * SF;
+ }
+ else
+ {
+ /* get horizontal scale factor */
+ Child(z, Down(y));
+ scale_factor = GetScaleFactor(z);
+ bc(constraint(x)) = scale_factor * SF;
+
+ /* get vertical scale factor */
+ Child(z, LastDown(y));
+ scale_factor = GetScaleFactor(z);
+ fc(constraint(x)) = scale_factor * SF;
+ }
+ DisposeChild(Down(x));
+ Child(y, LastDown(x));
+ y = Manifest(y, env, style, nbt, nft, target, crs, ok, FALSE, enclose, fcr);
+ ReplaceWithSplit(x, bthr, fthr);
+ break;
+
+
+ case KERN_SHRINK:
+
+ Child(y, Down(x));
+ y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ Child(y, LastDown(x));
+ y = Manifest(y, env, style, nbt, nft, target, crs, ok, FALSE, enclose, fcr);
+ ReplaceWithSplit(x, bthr, fthr);
+ break;
+
+
+ case YIELD:
+
+ Error(8, 29, "%s not expected here", FATAL, &fpos(x), KW_YIELD);
+ break;
+
+
+ case RAW_VERBATIM:
+ case VERBATIM:
+
+ Child(y, Down(x));
+ y = Manifest(y, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ DeleteLink(Down(x));
+ MergeNode(y, x); x = y;
+ break;
+
+
+ case CASE:
+
+ x = ManifestCase(x,env,style, bthr, fthr, target, crs, ok, need_expand, enclose, fcr);
+ break;
+
+
+ case BACKEND:
+
+ res = MakeWord(WORD, BackEndWord, &fpos(x));
+ ReplaceNode(res, x);
+ DisposeObject(x);
+ x = Manifest(res, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ break;
+
+
+ case XCHAR:
+
+ Child(y, Down(x));
+ y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ y = ReplaceWithTidy(y, FALSE);
+ if( !is_word(type(y)) )
+ { Error(8, 30, "%s dropped (parameter is not a simple word)",
+ WARN, &fpos(y), KW_XCHAR);
+ res = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ }
+ else if( (word_font(y) = font(*style)) == 0 )
+ { Error(8, 31, "%s dropped (no current font at this point)",
+ WARN, &fpos(y), KW_XCHAR);
+ res = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ }
+ else
+ { ch = MapCharEncoding(string(y), FontMapping(word_font(y), &fpos(y)));
+ if( ch == '\0' )
+ { type(y) = QWORD;
+ Error(8, 32, "%s dropped (character %s unknown in font %s)",
+ WARN, &fpos(y), KW_XCHAR, StringQuotedWord(y),
+ FontFamilyAndFace(word_font(y)));
+ res = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ }
+ else
+ { res = MakeWord(QWORD, STR_SPACE, &fpos(x));
+ string(res)[0] = ch;
+ }
+ }
+ ReplaceNode(res, x);
+ DisposeObject(x);
+ x = Manifest(res, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ break;
+
+
+ case CURR_LANG:
+
+ if( language(*style) == 0 )
+ { Error(8, 33, "no current language at this point, using %s",
+ WARN, &fpos(x), STR_NONE);
+ res = MakeWord(WORD, STR_NONE, &fpos(x));
+ }
+ else res = MakeWord(WORD, LanguageString(language(*style)), &fpos(x));
+ ReplaceNode(res, x);
+ DisposeObject(x);
+ x = Manifest(res, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ break;
+
+
+ case CURR_FAMILY:
+ case CURR_FACE:
+
+ if( font(*style) == 0 )
+ { Error(8, 38, "no current font at this point, using %s",
+ WARN, &fpos(x), STR_NONE);
+ res = MakeWord(WORD, STR_NONE, &fpos(x));
+ }
+ else if( type(x) == CURR_FAMILY )
+ res = MakeWord(WORD, FontFamily(font(*style)), &fpos(x));
+ else
+ res = MakeWord(WORD, FontFace(font(*style)), &fpos(x));
+ ReplaceNode(res, x);
+ DisposeObject(x);
+ x = Manifest(res, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ break;
+
+
+ case FONT:
+ case SPACE:
+ case YUNIT:
+ case ZUNIT:
+ case BREAK:
+ case COLOUR:
+ case LANGUAGE:
+
+ assert( Down(x) != x && NextDown(Down(x)) != x, "Manifest: FONT!" );
+ StyleCopy(new_style, *style);
+ Child(y, Down(x));
+ y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ y = ReplaceWithTidy(y, type(x) == COLOUR);
+ switch( type(x) )
+ {
+ case FONT: FontChange(&new_style, y);
+ break;
+
+ case SPACE: SpaceChange(&new_style, y);
+ break;
+
+ case YUNIT: YUnitChange(&new_style, y);
+ break;
+
+ case ZUNIT: ZUnitChange(&new_style, y);
+ break;
+
+ case BREAK: BreakChange(&new_style, y);
+ break;
+
+ case COLOUR: ColourChange(&new_style, y);
+ break;
+
+ case LANGUAGE: LanguageChange(&new_style, y);
+ break;
+
+ }
+ DisposeChild(Down(x));
+ Child(y, Down(x));
+ y = Manifest(y, env, &new_style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ DeleteLink(Down(x));
+ MergeNode(y, x); x = y;
+ break;
+
+
+ case PADJUST:
+ case HADJUST:
+ case VADJUST:
+
+ StyleCopy(new_style, *style);
+ if( type(x) == VADJUST ) vadjust(new_style) = TRUE;
+ else if( type(x) == HADJUST ) hadjust(new_style) = TRUE;
+ else padjust(new_style) = TRUE;
+ Child(y, Down(x));
+ y = Manifest(y, env, &new_style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ DeleteLink(Down(x));
+ MergeNode(y, x); x = y;
+ break;
+
+
+ case UNDERLINE:
+
+ /* change x to an ACAT and set the underline flags in its child */
+ assert( Down(x) != x && NextDown(Down(x)) == x, "Manifest: UNDERLINE!" );
+ type(x) = ACAT;
+ adjust_cat(x) = padjust(*style);
+ padjust(*style) = FALSE;
+ StyleCopy(save_style(x), *style);
+ Child(y, Down(x));
+ y = Manifest(y, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ SetUnderline(x);
+ break;
+
+
+ case MELD:
+ case COMMON:
+ case RUMP:
+
+ assert( Down(x) != x && NextDown(Down(x)) != x, "Manifest: COMMON!" );
+ debug2(DHY, DDD, "[Manifest %s %s", EchoObject(x), EchoObject(env));
+
+ /* find the first child of x, make sure it is an ACAT, and manifest */
+ Child(x1, Down(x));
+ if( type(x1) != ACAT )
+ { OBJECT newx1;
+ New(newx1, ACAT);
+ adjust_cat(newx1) = padjust(*style);
+ padjust(*style) = FALSE;
+ MoveLink(Down(x), newx1, CHILD);
+ Link(newx1, x1);
+ x1 = newx1;
+ }
+ x1 = Manifest(x1, env, style, nbt, nft, target, crs, ok, FALSE, enclose, fcr);
+ link1 = x1;
+ while( NextDown(link1) != x1 )
+ { Child(z, NextDown(link1));
+ if( type(z) == ACAT )
+ { TransferLinks(Down(z), z, NextDown(link1));
+ DisposeChild(Up(z));
+ }
+ else link1 = NextDown(link1);
+ }
+ debug1(DHY, DDD, " manifested x1 = %s", EchoObject(x1));
+
+ /* find the second child of x, make sure it is an ACAT, and manifest */
+ Child(x2, NextDown(Down(x)));
+ if( type(x2) != ACAT )
+ { OBJECT newx2;
+ New(newx2, ACAT);
+ adjust_cat(newx2) = padjust(*style);
+ padjust(*style) = FALSE;
+ MoveLink(NextDown(Down(x)), newx2, CHILD);
+ Link(newx2, x2);
+ x2 = newx2;
+ }
+ x2 = Manifest(x2, env, style, nbt, nft, target, crs, ok, FALSE, enclose, fcr);
+ link2 = x2;
+ while( NextDown(link2) != x2 )
+ { Child(z, NextDown(link2));
+ if( type(z) == ACAT )
+ { TransferLinks(Down(z), z, NextDown(link2));
+ DisposeChild(Up(z));
+ }
+ else link2 = NextDown(link2);
+ }
+ debug1(DHY, DDD, " manifested x2 = %s", EchoObject(x2));
+
+ if( type(x) == MELD )
+ {
+ /* if Meld, result is Meld(x1, x2) */
+ res = Meld(x1, x2);
+ }
+ else
+ {
+
+ /* find the point where x1 and x2 begin to differ */
+ link1 = Down(x1);
+ link2 = Down(x2);
+ while( link1 != x1 && link2 != x2 )
+ {
+ Child(y1, link1);
+ Child(y2, link2);
+ debug1(DHY, DDD, " y1 = %s", EchoObject(y1));
+ debug1(DHY, DDD, " y2 = %s", EchoObject(y2));
+ if( is_word(type(y1)) && is_word(type(y2)) )
+ {
+ if( !StringEqual(string(y1), string(y2)) ) break;
+ }
+ else if( type(y1) != type(y2) ) break;
+ link1 = NextDown(link1);
+ link2 = NextDown(link2);
+ }
+
+ /* if COMMON, result is x1 or x2 if either ran out, */
+ /* or else x2 (say) up to but not including link2 and prec gap */
+ if( type(x) == COMMON )
+ { if( link2 == x2 )
+ { res = x2;
+ }
+ else if( link1 == x1 )
+ { res = x1;
+ }
+ else
+ { if( link2 == Down(x2) )
+ res = MakeWord(WORD, STR_EMPTY, &fpos(x2));
+ else
+ { TransferLinks(PrevDown(link2), x2, x1);
+ res = x2;
+ }
+ }
+ }
+
+ /* if RUMP, result is x2 starting from link2 or NextDown(link2) */
+ else if( type(x) == RUMP )
+ { if( link2 == x2 )
+ res = MakeWord(WORD, STR_EMPTY, &fpos(x2));
+ else if( link1 == x1 )
+ {
+ TransferLinks(Down(x2), NextDown(link2), x1);
+ res = x2;
+ }
+ else /* link1 != x1 */
+ {
+ TransferLinks(Down(x2), link2, x1);
+ res = x2;
+ }
+ }
+ }
+
+ /* now res replaces x */
+ ReplaceNode(res, x);
+ DisposeObject(x);
+ x = res;
+ ReplaceWithSplit(x, bthr, fthr);
+ debug1(DHY, DDD, "]Manifest returning %s", EchoObject(x));
+ break;
+
+
+ case INSERT:
+
+ /* manifest and detach the left parameter, call it z */
+ Child(z, Down(x));
+ z = Manifest(z, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ DeleteLink(Down(x));
+
+ /* manifest the right parameter and make it the result */
+ Child(y, LastDown(x));
+ y = Manifest(y, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ DeleteLink(LastDown(x));
+ MergeNode(y, x); x = y;
+
+ /* now find the reattachment point for z down inside the result, x */
+ x = InsertObject(x, &z, style);
+ if( z != nilobj )
+ { Error(8, 34, "object dropped by %s: no suitable insert point", WARN,
+ &fpos(x), KW_INSERT);
+ DisposeObject(z);
+ }
+ break;
+
+
+ case ONE_OF:
+
+ Child(y, Down(x));
+ if( type(y) != ACAT )
+ {
+ /* child is not a sequence of choices, so ignore ONE_OF */
+ Error(8, 39, "%s ignored: no choices in right parameter", WARN,
+ &fpos(x), KW_ONE_OF);
+ y = Manifest(y, env, style, bthr, fthr, target, crs, ok, FALSE,
+ enclose, fcr);
+ DeleteLink(Down(x));
+ MergeNode(y, x); x = y;
+ }
+ else
+ {
+ /* try each child in turn; result is first to find *target */
+ OBJECT target_before;
+ for( link = Down(y); link != y; link = NextDown(link) )
+ {
+ Child(z, link);
+ if( type(z) == GAP_OBJ ) continue;
+ target_before = *target;
+ z = Manifest(z, env, style, bthr, fthr, target, crs, ok, FALSE,
+ enclose, fcr);
+ if( *target != target_before )
+ break;
+ }
+ DeleteLink(Up(z));
+ ReplaceNode(z, x);
+ DisposeObject(x);
+ x = z;
+ }
+ break;
+
+
+ case NEXT:
+
+ assert( Down(x) != x, "Manifest/NEXT: Down(x) == x!" );
+ Child(y, Down(x));
+ debug1(DCS, DD, " Manifesting Next( %s, 1 )", EchoObject(y));
+ y = Manifest(y, env, style, bthr, fthr, target, crs, FALSE, FALSE, enclose, fcr);
+ debug1(DCS, DD, " calling Next( %s, 1 )", EchoObject(y));
+ done = FALSE;
+ y = Next(y, 1, &done);
+ debug2(DCS, DD, " Next(done = %s) returning %s",
+ bool(done), EchoObject(y));
+ DeleteLink(Down(x));
+ MergeNode(y, x); x = y;
+ break;
+
+
+ case PLUS:
+ case MINUS:
+
+ Child(y, Down(x));
+ y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ y = ReplaceWithTidy(y, FALSE);
+ Child(z, NextDown(Down(x)));
+ z = Manifest(z, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ z = ReplaceWithTidy(z, FALSE);
+ if( is_word(type(y)) && sscanf( (char *) string(y), "%d", &num1) == 1 &&
+ is_word(type(z)) && sscanf( (char *) string(z), "%d", &num2) == 1 )
+ {
+ FULL_CHAR buff[MAX_BUFF];
+ sprintf( (char *) buff, "%d", type(x) == PLUS ? num1+num2 : num1-num2);
+ res = MakeWord(WORD, buff, &fpos(x));
+ }
+ else
+ { res = MakeWord(WORD, STR_NOCROSS, &fpos(x));
+ }
+ debug4(DOM, DD, "{ %s } %s { %s } = %s", EchoObject(y), Image(type(x)),
+ EchoObject(z), EchoObject(res));
+ res = Manifest(res, env, style, bthr, fthr, target, crs, FALSE, FALSE, enclose, fcr);
+ ReplaceNode(res, x);
+ DisposeObject(x);
+ x = res;
+ break;
+
+
+ case OPEN:
+
+ debug0(DCR, DD, " [ Manifest/OPEN begins:");
+ Child(y, Down(x));
+ DeleteLink(Down(x));
+ Child(res, LastDown(x));
+ hold_env = nilobj;
+ if( type(y) == CLOSURE )
+ { AttachEnv(env, y);
+ StyleCopy(save_style(y), *style);
+ debug0(DCR, DDD, "calling SetEnv from Manifest (b)");
+ res_env = SetEnv(y, nilobj);
+ New(hold_env, ACAT); Link(hold_env, res_env);
+ res = Manifest(res, res_env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ }
+ else if( is_cross(type(y)) )
+ { Child(z, Down(y));
+ if( type(z) == CLOSURE )
+ { debug0(DCR, DD, " calling CrossExpand from Manifest/OPEN");
+ /* *** want cross ref now always, not only if ok
+ y = CrossExpand(y, env, style, ok, crs, &res_env);
+ *** */
+ y = CrossExpand(y, env, style, crs, &res_env);
+ AttachEnv(res_env, y);
+ debug0(DCR, DDD, "calling SetEnv from Manifest (c)");
+ res_env = SetEnv(y, env);
+ New(hold_env, ACAT); Link(hold_env, res_env);
+ res = Manifest(res, res_env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ }
+ else
+ { Error(8, 35, "invalid left parameter of %s", WARN, &fpos(y), KW_OPEN);
+ res = Manifest(res, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ }
+ }
+ else
+ { Error(8, 36, "invalid left parameter of %s", WARN, &fpos(y), KW_OPEN);
+ res = Manifest(res, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ }
+ ReplaceNode(res, x);
+ DisposeObject(x);
+ if( hold_env != nilobj ) DisposeObject(hold_env);
+ x = res;
+ debug0(DCR, DD, " ] Manifest/OPEN ends");
+ break;
+
+
+ case TAGGED:
+
+ x = ManifestTg(x, env, style, bthr, fthr, target, crs, ok, need_expand, enclose, fcr);
+ debug2(DCR, DD, "Manifest returning %ld %s", (long) x, EchoObject(x));
+ break;
+
+
+ case PLAIN_GRAPHIC:
+ case GRAPHIC:
+
+ debug1(DRS, DD, " graphic style in Manifest = %s", EchoStyle(style));
+ Child(y, LastDown(x));
+ y = Manifest(y, env, style, nbt, nft, target, crs, ok, FALSE, enclose, fcr);
+ StyleCopy(save_style(x), *style);
+ Child(y, Down(x));
+ y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ ReplaceWithSplit(x, bthr, fthr);
+ break;
+
+
+ case INCGRAPHIC:
+ case SINCGRAPHIC:
+
+ StyleCopy(save_style(x), *style);
+ debug2(DGP, DD, " manifest at %s (style %s)",
+ EchoObject(x), EchoStyle(&save_style(x)));
+ Child(y, Down(x));
+ y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, fcr);
+ y = ReplaceWithTidy(y, FALSE);
+ if( !is_word(type(y)) )
+ { Error(8, 37, "%s deleted (invalid right parameter)", WARN, &fpos(y),
+ type(x) == INCGRAPHIC ? KW_INCGRAPHIC : KW_SINCGRAPHIC);
+ y = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ ReplaceNode(y, x); DisposeObject(x);
+ x = Manifest(y, env, style, bthr, fthr, target, crs, ok, FALSE, enclose, fcr);
+ return x;
+ }
+ ReplaceWithSplit(x, bthr, fthr);
+ break;
+
+
+ default:
+
+ assert1(FALSE, "Manifest:", Image(type(x)));
+ break;
+
+ } /* end switch */
+
+ debug2(DOM, DD, "]Manifest returning %s %s", Image(type(x)), EchoObject(x));
+ debug1(DOM, DD, " at exit, style = %s", EchoStyle(style));
+ debug1(DOM, DDD, "up: ", EchoObject(bthr[COLM]));
+ debug1(DOM, DDD, "down: ", EchoObject(fthr[COLM]));
+ debug1(DOM, DDD, "left: ", EchoObject(bthr[ROWM]));
+ debug1(DOM, DDD, "right: ", EchoObject(fthr[ROWM]));
+ debugcond1(DHY, DD, eee, "] Manifest returning %s", EchoObject(x));
+ depth--;
+ return x;
+} /* end Manifest */