OBJECT Manifest()

in MultiSource/Benchmarks/MiBench/consumer-typeset/z08.c [881:2068]


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_outline(x) = outline(*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_outline(y) = outline(*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_outline(y) = outline(*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_outline(prev) == word_outline(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_outline(y) = word_outline(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 */
      /* *** this has been moved now to MinSize, z12.c *** */

      /* ***
      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_outline(z) = outline(*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 BEGIN_HEADER:
    case SET_HEADER:

      /* first manifest gap, which is left parameter */
      Child(y, Down(x));
      y = Manifest(y, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE,
	&nenclose, fcr);
      y = ReplaceWithTidy(y, FALSE);
      GetGap(y, style, &line_gap(save_style(x)), &res_inc);

      /* now the right parameter */
      Child(y, LastDown(x));
      y = Manifest(y, env, style, bthr, fthr, target, crs, ok, need_expand,
	enclose, fcr);
      break;


    case END_HEADER:
    case CLEAR_HEADER:

      /* give these objects a dummy child, just so that threads can attach */
      /* to it and keep the thread code happy.  Don't use ReplaceWithSplit */
      /* because we don't want a split above a header                      */
      y = MakeWord(WORD, STR_EMPTY, &fpos(x));
      Link(x, y);
      y = Manifest(y, env, style, bthr, fthr, target, crs, ok, need_expand,
	enclose, fcr);
      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, BackEnd->name, &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 CURR_YUNIT:
    case CURR_ZUNIT:

      { FULL_CHAR buff[20];
        if( type(x) == CURR_YUNIT )
          sprintf( (char *) buff, "%dp", yunit(*style) / PT);
        else
	  sprintf( (char *) buff, "%dp", zunit(*style) / PT);
        res = MakeWord(WORD, buff, &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 OUTLINE:
    case PADJUST:
    case HADJUST:
    case VADJUST:

      StyleCopy(new_style, *style);
      if(      type(x) == OUTLINE )  outline(new_style) = TRUE;
      else 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, nbt, nft, target, crs, ok, FALSE, enclose, fcr);
      SetUnderline(x);
      ReplaceWithSplit(x, bthr, fthr);
      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 LINK_SOURCE:
    case LINK_DEST:

      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);
      y = ReplaceWithTidy(y, TRUE);
      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 */