aboutsummaryrefslogblamecommitdiffstats
path: root/z22.c
blob: 859e363052a6d09fd619ea1a296802dc674b5aaf (plain) (tree)
1
2
3
4
5
6
7
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333

                                                                               

                                                                               
                                                                               
                                                                               
                                                                               




                                                                               
                                                                               


















                                                                               

                   
 




















                                                                               



































                                                                               
                                                                   








































                                                                       

                         







                                                   
                                                           







                                                           

                                                                      













                                                                 
                                                                                
                                


                                                                          



































                                                                                
                                            













                                                                               
                                                                                  
                                             






                                                                          
                                                                    




                                                                         

                                           




                                                      
                            




                                                                           
                                                                              

                                       
                                                                       

                                         
                                                                    
                                               
                                                                     





                                                                                  
                                                   






                                                     
                                                             





                                                                                  
                                                     











                                                                     
                                                                     









                                                                    
                                                  










                                                                               
                          

























                                                                             






                            

                   

                      









                     

                 

















                       

                     
                        
                  
















                                                                                
 

                                                                               















                                                                               






                                                                               









                                                                          
     



                          










                                                                               





                                                                               

                                                                               
                                           

                             
                                                                 

                                                                                     
                                                                 

                                                              





                                         
                         




                    

                                                                        




                        



















                                                                  




                    
                                          
       


















                                                                                  




            






                                                                              
                               


                           
 
                                                                         

                                                     


                        












                                                                               



                                                                               



                                                                                

                                                  
                          
                 

                                                       
                                                  









                                                                              
                            
                                                      
     







































































                                                                                
                                                               
                                             
                                             
                                               
                                                       
                                         
                                                 






                                                                               






































































                                                                           


                                                           


                                                                            

                                      

                                 

                                                                  




































































                                                                               










                                                                           







                     

                     






















                           

                         
                      





                       
                            









                                                                 

                                                                                
                                                                            
                                                
 











                                                                               
                                                                              

                                                                       
                                                               
                       

                                                                          

                            


                                                                       





                                                                                






                                                                                
                                                            


















































                                                                               


                                                    
                               

                                                                      


                                  



                                                           
                                  










                                                                       

                                                  















































































































































































































































































































                                                                               
/*@z22.c:Galley Service:Interpose()@******************************************/
/*                                                                           */
/*  THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.41)                       */
/*  COPYRIGHT (C) 1991, 2023 Jeffrey H. Kingston                             */
/*                                                                           */
/*  Jeffrey H. Kingston (jeff@it.usyd.edu.au)                                */
/*  School of Information Technologies                                       */
/*  The University of Sydney 2006                                            */
/*  AUSTRALIA                                                                */
/*                                                                           */
/*  This program is free software; you can redistribute it and/or modify     */
/*  it under the terms of the GNU General Public License as published by     */
/*  the Free Software Foundation; either Version 3, or (at your option)      */
/*  any later version.                                                       */
/*                                                                           */
/*  This program is distributed in the hope that it will be useful,          */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
/*  GNU General Public License for more details.                             */
/*                                                                           */
/*  You should have received a copy of the GNU General Public License        */
/*  along with this program; if not, write to the Free Software              */
/*  Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA   */
/*                                                                           */
/*  FILE:         z22.c                                                      */
/*  MODULE:       Galley Service                                             */
/*  EXTERNS:      Interpose(), FlushInners(), ExpandRecursives(),            */
/*                Promote(), KillGalley(), FreeGalley(),                     */
/*                SetTarget(), CheckComponentOrder()                         */
/*                                                                           */
/*****************************************************************************/
#include "externs.h"
#include "parent.h"
#include "child.h"

/* these three variables refer to the root galley only */
static BOOLEAN first = TRUE;		  /* if first component unwritten    */
static OBJECT page_label = nilobj;	  /* current page label object       */
static OBJECT prev_page_label = nilobj; /* previous page label object      */


/*****************************************************************************/
/*                                                                           */
/*  void PromoteInit(void)                                                   */
/*                                                                           */
/*  Initialize this module.                                                  */
/*                                                                           */
/*****************************************************************************/

void PromoteInit(void)
{
  first = TRUE;
  page_label = nilobj;
  prev_page_label = nilobj;
}


/*****************************************************************************/
/*                                                                           */
/*  Interpose(z, typ, x, y)                                                  */
/*                                                                           */
/*  Insert a new typ object above z.  Its sizes are to be taken from x       */
/*  (column) and y (row).                                                    */
/*                                                                           */
/*****************************************************************************/

void Interpose(OBJECT z, int typ, OBJECT x, OBJECT y)
{ OBJECT encl;
  New(encl, typ);
  FposCopy(fpos(encl), fpos(y));
  ReplaceNode(encl, z);  Link(encl, z);
  back(encl, COLM) = back(x, COLM);
  fwd(encl, COLM) = fwd(x, COLM);
  back(encl, ROWM) = back(y, ROWM);
  fwd(encl, ROWM) = fwd(y, ROWM);
  underline(encl) = underline(z);
} /* end Interpose */


/*@::FlushInners()@***********************************************************/
/*                                                                           */
/*  FlushInners(inners, hd)                                                  */
/*                                                                           */
/*  Flush each galley on the list inners.  These have become flushable       */
/*  by being promoted off the top of galley hd; if hd is the root galley,    */
/*  identifiable by having PrintSym as target, do not flush inners at all.   */
/*                                                                           */
/*****************************************************************************/

void FlushInners(OBJECT inners, OBJECT hd)
{ OBJECT y, z, tmp, dest_index;

  debug1(DGA, D, "[ FlushInners(%s, -)", DebugInnersNames(inners));
  ifdebug(DGF, D,
    OBJECT link;
    fprintf(stderr, "dgf: [ FlushInners(");
    for( link = Down(inners);  link != inners;  link = NextDown(link) )
    {
      Child(y, link);
      fprintf(stderr, " %s", Image(type(y)));
      switch( type(y) )
      {

        case DEAD:
      
	  break;


        case RECEIVING:
        case UNATTACHED:
      
	  if( Down(y) != y )	/* bug fix (was assert before) */
	  { assert( Down(y) != y, "FlushInners: UNATTACHED!");
	    Child(z, Down(y));
	    fprintf(stderr, " %s", SymName(actual(z)));
	  }
	  break;


        case PRECEDES:
      
	  break;


        case GALL_PREC:

	  break;


        default:
      
	  break;
      }
    }
    fprintf(stderr, ")");
    debug0(DGF, D, "");
  )

  /* check for root galley case */
  if( hd != nilobj )
  { assert( Up(hd) != hd, "FlushInners: Up(hd)!" );
    Parent(dest_index, Up(hd));
    if( actual(actual(dest_index)) == PrintSym )
    { DisposeObject(inners);
      debug0(DGA, D, "] FlushInners returning (PrintSym)");
      debug0(DGF, D, "] FlushInners returning (PrintSym)");
      return;
    }
  }

  while( Down(inners) != inners )
  { Child(y, Down(inners));
    DeleteLink(Down(inners));
    debug2(DGA, D, "FlushInners at %s (remainder %s)", Image(type(y)),
      DebugInnersNames(inners));
    switch( type(y) )
    {

      case DEAD:
      
	break;


      case RECEIVING:
      case UNATTACHED:
      
	if( Down(y) != y )	/* bug fix (was assert before) */
	{ assert( Down(y) != y, "FlushInners: UNATTACHED!");
	  Child(z, Down(y));
	  debug1(DGA,D,"  possibly calling FlushGalley %s from FlushInners (a)",
	    SymName(actual(z)));
	  if( whereto(z) != nilobj )
	    debug2(DGA,D,"    (whereto(z) = %s, uses_extern_target = %s)",
	      SymName(whereto(z)), bool(uses_extern_target(whereto(z))));
	  if( whereto(z)==nilobj || !uses_extern_target(whereto(z)) ) /* &&& */
	    FlushGalley(z);
	}
	break;


      case PRECEDES:
      
	Child(tmp, Down(y));
	if( Up(tmp) != LastUp(tmp) )
	{ Parent(tmp, LastUp(tmp));
	  assert(type(tmp)==FOLLOWS, "FlushInners: FOLLOWS!");
	  if( blocked(tmp) )
	  { blocked(tmp) = FALSE;
	    Parent(z, Up(tmp));
	    debug0(DGF, D, "  calling FlushGalley from FlushInners (b)");
	    if( whereto(z)==nilobj || !uses_extern_target(whereto(z)) )/* &&& */
	      FlushGalley(z);
	  }
	}
	break;


      case GALL_PREC:

	/* someone else is looking after this now */
	break;


      default:
      
	assert1(FALSE, "FlushInners:", Image(type(y)));
	break;
    }
  }
  Dispose(inners);
  debug0(DGA, D, "] FlushInners returning");
  debug0(DGF, D, "] FlushInners returning");
} /* end FlushInners */


/*@::ExpandRecursives()@******************************************************/
/*                                                                           */
/*  ExpandRecursives(recs)                                                   */
/*                                                                           */
/*  Expand each of the recursive definite objects in the list recs.          */
/*                                                                           */
/*****************************************************************************/

void ExpandRecursives(OBJECT recs)
{ CONSTRAINT non_c, hc, vc;
  OBJECT target_index, target, z = nilobj, n1, inners, newrecs, hd, tmp, env, why;
  debug0(DCR, DDD, "ExpandRecursives(recs)");
  SetConstraint(non_c, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
  n1 = nilobj;
  assert(recs != nilobj, "ExpandRecursives: recs == nilobj!");
  while( Down(recs) != recs )
  { Child(target_index, Down(recs));  DeleteLink( Down(recs) );
    assert( type(target_index) == RECURSIVE, "ExpandRecursives: index!" );
    target = actual(target_index);
    debug2(DCR, DDD, "  expanding %s %s", Image(type(target_index)),
      EchoObject(target));

    /* expand body of target, convert to galley, and check size */
    New(hd, HEAD);  actual(hd) = actual(target);  must_expand(hd) = TRUE;
    force_gall(hd) = FALSE;
    ClearHeaders(hd);
    enclose_obj(hd) = limiter(hd) = nilobj;
    opt_components(hd) = opt_constraints(hd) = nilobj;
    gall_dir(hd) = horiz_galley(actual(target));
    whereto(hd) = ready_galls(hd) = nilobj;
    foll_or_prec(hd) = GALL_FOLL;
    sized(hd) = FALSE;
    seen_nojoin(hd) = FALSE;
    tmp =  CopyObject(target, &fpos(target));  env = DetachEnv(tmp);
    Link(hd, tmp);  Link(target_index, hd);
    SizeGalley(hd, env, external_ver(target),
      gall_dir(hd) == ROWM ? threaded(target) : FALSE, FALSE, FALSE,
      &save_style(target), &non_c, nilobj, &n1, &newrecs, &inners, nilobj);
    debug1(DGA, D, "  ExpandRecursives inners: %s", DebugInnersNames(inners));
    debug0(DCR, DDD, "    as galley:");
    ifdebug(DCR, DDD, DebugObject(hd));
    debug1(DGS, DD, "[ ExpandRecursives calling Constrained(%s, COLM)",
      EchoObject(target));
    Constrained(target, &hc, COLM, &why);
    debug2(DGS, DD, "] ExpandRecursives Constrained(%s, COLM) = %s",
      EchoObject(target), EchoConstraint(&hc));
    debug3(DCR, DDD, "    horizontal size: (%s, %s); constraint: %s",
      EchoLength(back(hd, COLM)), EchoLength(fwd(hd, COLM)), EchoConstraint(&hc));
    if( !FitsConstraint(back(hd, COLM), fwd(hd, COLM), hc) )
    { DisposeChild(Up(hd));
      if( inners != nilobj ) DisposeObject(inners);
      if( newrecs != nilobj ) DisposeObject(newrecs);
      DeleteNode(target_index);
      debug0(DCR, DDD, "    rejecting (too wide)");
      continue;
    }
    if( !external_ver(target) )
    { Constrained(target, &vc, ROWM, &why);
      debug2(DSC, DD, "Constrained( %s, ROWM ) = %s",
	EchoObject(target), EchoConstraint(&vc));
      Child(z, LastDown(hd));
      debug3(DCR, DDD, "    vsize: (%s, %s); constraint: %s",
	EchoLength(back(z, ROWM)), EchoLength(fwd(z, ROWM)), EchoConstraint(&vc));
      if( !FitsConstraint(back(z, ROWM), fwd(z, ROWM), vc) )
      {	DisposeChild(Up(hd));
	if( inners != nilobj ) DisposeObject(inners);
	if( newrecs != nilobj ) DisposeObject(newrecs);
	DeleteNode(target_index);
	debug0(DCR, DDD, "    rejecting (too high)");
	continue;
      }
    }

    /* object fits; adjust sizes and promote */
    debug0(DSA, D, "calling AdjustSize from ExpandRecursives (a)");
    AdjustSize(target, back(hd, COLM), fwd(hd, COLM), COLM);
    if( !external_ver(target) )
    { debug0(DSA, D, "calling AdjustSize from ExpandRecursives (b)");
      AdjustSize(target, back(z, ROWM), fwd(z, ROWM), ROWM);
      Interpose(target, VCAT, z, z);
    }
    debug0(DGS, DD, "calling Promote(hd, hd) from ExpandRecursives");
    Promote(hd, hd, target_index, TRUE);  DeleteNode(hd);
    DeleteNode(target_index);
    if( inners != nilobj )
    {
      debug0(DGF, D, "  calling FlushInners from ExpandRecursives");
      FlushInners(inners, nilobj);
    }
    if( newrecs != nilobj )  MergeNode(recs, newrecs);
  } /* end while */
  Dispose(recs);
  debug0(DCR, DDD, "ExpandRecursives returning.");
} /* end ExpandRecursives */

/*@::FindSplitInGalley()@*****************************************************/
/*                                                                           */
/*  static OBJECT FindSplitInGalley(hd)                                      */
/*                                                                           */
/*  Search simply joined galley hd for a SPLIT object, which must be there.  */
/*                                                                           */
/*****************************************************************************/

static OBJECT FindSplitInGalley(OBJECT hd)
{ OBJECT link, y = nilobj;
  debug0(DGF, D, "FindSplitInGalley(hd)");
  for( link = Down(hd);  link != hd;  link = NextDown(link) )
  { Child(y, link);
    if( is_definite(type(y)) )  break;
  }
  if( link == hd )
  { debug0(DGF, D, "FindSplitInGalley failing, no definite component; hd =");
    ifdebug(DGF, D, DebugObject(hd));
    Error(22, 1, "FindSplit: missing galley component", INTERN, &fpos(hd));
  }
  while( type(y) != SPLIT )  switch( type(y) )
  {
    case VCAT:
    case ONE_ROW:
    case WIDE:
    case HIGH:
    case HSHIFT:
    case VSHIFT:
    case VCONTRACT:
    case VLIMITED:
    case VEXPAND:

      Child(y, Down(y));
      break;


    case BEGIN_HEADER:
    case SET_HEADER:

      Child(y, LastDown(y));
      break;


    case CLOSURE:
    case NULL_CLOS:
    case END_HEADER:
    case CLEAR_HEADER:
    case PAGE_LABEL:
    case HCAT:
    case WORD:
    case QWORD:
    case ACAT:
    case ROW_THR:
    case COL_THR:
    case ONE_COL:
    case SCALE:
    case KERN_SHRINK:
    case HMIRROR:
    case VMIRROR:
    case HSCALE:
    case VSCALE:
    case HCOVER:
    case VCOVER:
    case HCONTRACT:
    case HLIMITED:
    case HEXPAND:
    case START_HVSPAN:
    case START_HSPAN:
    case START_VSPAN:
    case HSPAN:
    case VSPAN:
    case ROTATE:
    case BACKGROUND:
    case INCGRAPHIC:
    case SINCGRAPHIC:
    case PLAIN_GRAPHIC:
    case GRAPHIC:
    case LINK_SOURCE:
    case LINK_DEST:
    case LINK_DEST_NULL:
    case LINK_URL:

      debug0(DGF, D, "FindSplitInGalley(hd) failing, hd =");
      ifdebug(DGF, D, DebugObject(hd));
      Error(22, 2, "FindSplitInGalley failed", INTERN, &fpos(y),Image(type(y)));
      break;


    default:
    
      assert1(FALSE, "FindSplitInGalley:", Image(type(y)));
      break;

  }
  debug0(DGF, D, "FindSplitInGalley returning.");
  return y;
} /* end FindSplitInGalley */


/*****************************************************************************/
/*                                                                           */
/*  ClearHeaders(OBJECT hd)                                                  */
/*                                                                           */
/*  Clear the headers of hd.                                                 */
/*                                                                           */
/*****************************************************************************/

void ClearHeaders(OBJECT hd)
{ int i;
  for( i = 0;  i < MAX_HCOPIES;  i++ )
    headers(hd, i) = nilobj;
  head_next(hd) = 0;
} /* end ClearHeaders */


/*****************************************************************************/
/*                                                                           */
/*  DisposeHeaders(OBJECT hd)                                                */
/*                                                                           */
/*  Dispose the headers of hd.                                               */
/*                                                                           */
/*****************************************************************************/

static void DisposeHeaders(OBJECT hd)
{ int i;
  for( i = 0;  i < MAX_HCOPIES;  i++ )
  {
    if( headers(hd, i) != nilobj )
    { assert(type(headers(hd, i)) == ACAT || type(headers(hd, i)) == VCAT,
        "DisposeHeaders: type(headers(hd))!");
      while( Down(headers(hd, i)) != headers(hd, i) )
      { DisposeChild(Down(headers(hd, i)));
      }
      headers(hd, i) = nilobj;
    }
  }
} /* end DisposeHeaders */


/*@::HandleHeader()@**********************************************************/
/*                                                                           */
/*  HandleHeader(hd, link, header)                                           */
/*                                                                           */
/*  Given galley hd containing header object header, this routine handles    */
/*  the header, i.e. it updates headers(hd) to reflect the effect of the     */
/*  header, then deletes the header and its following gap object.            */
/*                                                                           */
/*  Link is the link from hd to the header (it may actually link to a        */
/*  split object which then links to the header, but no matter).             */
/*                                                                           */
/*  Actually, we no longer allow a SPLIT object above a header, because      */
/*  that means there is a COL_THR or ROW_THR above it and when we dispose    */
/*  the header, Dispose is not able to dispose the appropriate child of the  */
/*  COL_THR or ROW_THR.  So Manifest() must not insert a SPLIT above a       */
/*  header, and we check for that.                                           */
/*                                                                           */
/*****************************************************************************/

void HandleHeader(OBJECT hd, OBJECT header)
{ OBJECT g, gaplink, gap_obj;
  int i;
  debug3(DGS, D, "[ HandleHeader(%s, %s); headers = %s; galley:",
    SymName(actual(hd)), EchoObject(header), EchoObject(headers(hd, head_next(hd))));
  ifdebug(DGS, DD, DebugGalley(hd, nilobj, 2));
  assert(is_header(type(header)), "HandleHeader: type(header)!");
  assert(Up(header) == LastUp(header) && Up(header) != header,
    "HandleHeader: header parents!");
  switch( type(header) )
  {

    case CLEAR_HEADER:

      /* clear out old headers, if any */
      DisposeHeaders(hd);
      break;


    case SET_HEADER:

      /* clear out old headers (not safe to dispose them yet), if any */
      DisposeHeaders(hd);
      /* NB NO BREAK! */


    case BEGIN_HEADER:

      Child(gap_obj, Down(header));
      for( i = 0;  i < MAX_HCOPIES;  i++ )
      {
        /* make new headers if required */
        if( headers(hd, i) == nilobj )
	  New(headers(hd, i), gall_dir(hd) == ROWM ? VCAT : ACAT);

        /* construct a gap object from the left parameter */
        New(g, GAP_OBJ);
        underline(g) = FALSE;
	Link(g, CopyObject(gap_obj, &fpos(gap_obj)));
        GapCopy(gap(g), line_gap(save_style(header)));
        mark(gap(g)) = join(gap(g)) = FALSE;

        /* move header and gap into headers() */
        MoveLink(NextDown(Down(header)), headers(hd, i), PARENT);
        Link(headers(hd, i), g);
        debug2(DGS, D, "HandleHeader moving %s header %s",
	  SymName(actual(hd)), Image(type(header)));
      }
      break;


    case END_HEADER:

      for( i = 0;  i < MAX_HCOPIES;  i++ )
      {
        if( headers(hd, i) == nilobj )
	  Error(22, 11, "no header for %s to remove", WARN, &fpos(hd),
	    KW_END_HEADER);
        else
        {
	  /* dispose last gap */
	  assert(LastDown(headers(hd, i))!=headers(hd, i), "Promote/END_HEADER!");
	  Child(g, LastDown(headers(hd, i)));
	  assert(type(g) == GAP_OBJ, "HandleHeader: END_HEADER/gap!");
	  DisposeChild(LastDown(headers(hd, i)));

	  /* dispose last header object */
	  assert(LastDown(headers(hd, i))!=headers(hd, i), "Promote/END_HEADER!");
	  DisposeChild(LastDown(headers(hd, i)));
	  if( Down(headers(hd, i)) == headers(hd, i) )
	  { DisposeObject(headers(hd, i));
	    headers(hd, i) = nilobj;
	  }
        }
      }
      break;
	
  }

  /* dispose header object (must take care to disentangle safely) */
  gaplink = NextDown(Up(header));
  assert(type(gaplink) == LINK, "HandleHeader: type(gaplink)!");
  if( type(header) == CLEAR_HEADER || type(header) == END_HEADER )
  {
    /* first disentangle child properly */
    assert(Down(header) != header && Down(header) == LastDown(header), "HH!");
    DisposeChild(Down(header));
  }
  DisposeChild(Up(header));
  DisposeChild(gaplink);

  debug2(DGS, D, "] HandleHeader returning; headers(%s) now %s; galley:",
    SymName(actual(hd)), EchoObject(headers(hd, 0)));
  ifdebug(DGS, DD, DebugGalley(hd, nilobj, 2));
} /* end HandleHeader */


/*@::Promote()@***************************************************************/
/*                                                                           */
/*  Promote(hd, stop_link, dest_index, join_after)                           */
/*                                                                           */
/*  Promote components of galley hd into its destination (dest), up to but   */
/*  not including the one linked to hd by link stop_link, which always       */
/*  follows a component.  No size adjustments are made, except that when     */
/*  two col_thr nodes are merged, a COLM adjustment is made to the result.   */
/*                                                                           */
/*  If the galley is ending here, Promote inserts a gap at the end of it.    */
/*  Whether to make this a joining gap or not is a tricky question which     */
/*  Promote answers by referring to join_after.                              */
/*                                                                           */
/*  Any BEGIN_HEADER, END_HEADER, SET_HEADER, or CLEAR_HEADER objects in     */
/*  the promoted components are diverted to headers(hd), if the target       */
/*  is internal.                                                             */
/*                                                                           */
/*****************************************************************************/

void Promote(OBJECT hd, OBJECT stop_link, OBJECT dest_index, BOOLEAN join_after)
{
  OBJECT dest, link, y, z, tmp1, tmp2, why, top_y;
  FULL_CHAR *label_string, buff[MAX_LINE];
  FULL_LENGTH aback, afwd;
  int dim, pnval;
  debug2(DGS, DD, "[ Promote(%s width %s, stop_link):",
    SymName(actual(hd)), EchoLength(size(hd, COLM)));
  ifdebug(DGS, DD, DebugGalley(hd, stop_link, 2));

  assert( type(hd) == HEAD, "Promote: hd!" );
  assert( type(stop_link) == LINK || stop_link == hd, "Promote: stop_link!" );
  assert( stop_link != Down(hd), "Promote: stop_link == Down(hd)!" );
  type(dest_index) = RECEIVING;
  dest = actual(dest_index);

  /* insert final gap if galley is ending */
  if( stop_link != hd )
  { Child(y, stop_link);
    if( type(y) != GAP_OBJ )
    { ifdebug(DGS, DD, DebugGalley(hd, stop_link, 2));
    }
    assert( type(y) == GAP_OBJ, "Promote: missing GAP_OBJ!" );
    stop_link = NextDown(stop_link);
  }
  else
  { New(y, GAP_OBJ);
    FposCopy(fpos(y), fpos(hd));
    hspace(y) = 0;  vspace(y) = 1;
    /* SetGap(gap(y), FALSE, FALSE, seen_nojoin(hd), FIXED_UNIT, NO_MODE, 0); */
    /* SetGap(gap(y), FALSE, FALSE, threaded(dest), FIXED_UNIT, NO_MODE, 0); */
    /* SetGap(gap(y), FALSE, FALSE, TRUE, FIXED_UNIT, NO_MODE, 0); */
    /* ClearGap(gap(y)); */
    SetGap(gap(y), FALSE, FALSE, join_after, FIXED_UNIT, NO_MODE, 0);
    Link(stop_link, y);
  }

  /* if optimizing, add to dummy paragraph containing components and gaps */
  if( opt_components(hd) != nilobj )
  { OBJECT last, tmp;

    debug1(DOG, DD, "Promote(%s) optimizing:", SymName(actual(hd)));
    if( LastDown(opt_components(hd))!=opt_components(hd) && !opt_gazumped(hd) )
    {
      Child(last, LastDown(opt_components(hd)));
    }
    else last = nilobj;
    for( link = Down(hd);  link != stop_link;  link = NextDown(link) )
    { Child(y, link);
      if( type(y) == GAP_OBJ )
      {
	if( last == nilobj )
	{
	  /* do nothing, gap cannot separate definite objects */
	  debug1(DOG, DD, "  skipping initial GAP_OBJ %s", EchoGap(&gap(y)));
	}
	else if( type(last) == GAP_OBJ )
	{
	  /* previous gap must have preceded an indefinite, so overwrite it */
	  FposCopy(fpos(last), fpos(y));
	  debug2(DOG, DD, "  overwriting GAP_OBJ %s with %s",
	    EchoGap(&gap(last)), EchoGap(&gap(y)));
	  GapCopy(gap(last), gap(y));
	  if( Down(last) != last )  DisposeChild(Down(last));
	  if( Down(y) != y )
	  { Child(tmp, Down(y));
	    tmp = CopyObject(tmp, no_fpos);
	    Link(last, tmp);
	  }
	  join(gap(last)) = TRUE;  /* irrelevant but improves debug output */
	}
	else
	{
	  /* previous was definite, so this gap must be stored */
	  opt_gazumped(hd) = FALSE;
	  New(last, GAP_OBJ);
	  FposCopy(fpos(last), fpos(y));
	  GapCopy(gap(last), gap(y));
	  join(gap(last)) = TRUE;  /* irrelevant but improves debug output */
	  hspace(last) = 1;
	  vspace(last) = 0;
	  Link(opt_components(hd), last);
	  debug1(DOG, DD, "  adding GAP_OBJ %s", EchoGap(&gap(last)));
	}
      }
      else if( is_word(type(y)) )
      {
	/* definite, must be stored */
	opt_gazumped(hd) = FALSE;
	last = MakeWord(type(y), string(y), &fpos(y));
	back(last, COLM) = back(y, gall_dir(hd));
	fwd(last, COLM) = fwd(y, gall_dir(hd));
	word_font(last) = word_font(y);
	word_colour(last) = word_colour(y);
	word_underline_colour(last) = word_underline_colour(y);
	word_texture(last) = word_texture(y);
	word_outline(last) = word_outline(y);
	word_language(last) = word_language(y);
	word_baselinemark(last) = word_baselinemark(y);
	word_strut(last) = word_strut(y);
	word_ligatures(last) = word_ligatures(y);
	word_hyph(last) = word_hyph(y);
	Link(opt_components(hd), last);
	debug2(DOG, DD, "  adding %s \"%s\"", Image(type(last)), string(last));
      }
      else if( is_indefinite(type(y)) )
      {
	/* indefinite, always skip these */
      }
      else if( is_definite(type(y)) )
      {
	/* definite other than WORD, add it */
	opt_gazumped(hd) = FALSE;
	last = MakeWord(QWORD, AsciiToFull("w"), &fpos(y));
	back(last, COLM) = back(y, gall_dir(hd));
	fwd(last, COLM) = fwd(y, gall_dir(hd));
	Link(opt_components(hd), last);
	debug1(DOG, DD, "  adding word for %s", EchoObject(y));
      }
    }
    debug1(DOG, DD, "Promote(%s) end optimizing", SymName(actual(hd)));
  }

  /* error if promoting a seen_nojoin galley into a threaded destination */
  if( seen_nojoin(hd) && gall_dir(hd) == ROWM && threaded(dest) )
    Error(22, 3, "galley %s must have a single column mark",
      FATAL, &fpos(hd), SymName(actual(hd)));

  /* make nojoin status clear by adding an extra gap at start if needed */
  if( gall_dir(hd) == ROWM && !external_ver(dest) && seen_nojoin(hd) &&
      join(gap(y)) )
  { OBJECT prnt, extra_null, extra_gap;

    /* add nojoin gap at start */
    Parent(prnt, Up(dest));  /* can't be threaded */
    assert( type(prnt) == VCAT, "Promote: nojoin case, can't find VCAT" );
    New(extra_null, NULL_CLOS);
    back(extra_null, COLM) = fwd(extra_null, COLM) = 0;
    back(extra_null, ROWM) = fwd(extra_null, ROWM) = 0;
    New(extra_gap, GAP_OBJ);
    hspace(extra_gap) = vspace(extra_gap) = 0;
    SetGap(gap(extra_gap), FALSE, FALSE, FALSE, FIXED_UNIT, EDGE_MODE, 0);
    Link(Down(prnt), extra_gap);
    Link(Down(prnt), extra_null);
    debug0(DGS, DD, "  Promote adding extra nojoin gap");
    /* join(gap(y)) = FALSE; */
  }


  /* if promoting out of root galley, do special things */
  if( actual(dest) == PrintSym )
  { CONSTRAINT c;
    link = hd;
    while( NextDown(link) != stop_link )
    { Child(y, NextDown(link));
      debug2(DGS, DD, "root promote %s %s", Image(type(y)),
	is_definite(type(y)) ? STR_EMPTY : EchoObject(y));
      if( type(y) == SPLIT )  Child(y, DownDim(y, ROWM));
      switch( type(y) )
      {

	case SCALE_IND:
	case COVER_IND:
	case PRECEDES:
      
	  DisposeChild(NextDown(link));
	  break;
	

	case UNATTACHED:
      
	  assert( Down(y) != y, "FlushRootGalley: UNATTACHED!" );
	  Child(z, Down(y));
	  assert( type(z) == HEAD, "FlushRootGalley: unattached HEAD!" );
	  if( sized(z) )
	  {
	    /* galley is part flushed, leave it here */
	    link = NextDown(link);
	  }
	  /* ??? else if( foll_or_prec(z) == GALL_PREC ) */
	  else if( foll_or_prec(z) == GALL_PREC ||
		   foll_or_prec(z) == GALL_FOLL_OR_PREC )
	  {
	    /* galley is preceding or foll_or_prec, send to CrossSequence */
	    OBJECT t;
	    /* type(y) = GALL_PREC; */
	    type(y) = foll_or_prec(z);
	    pinpoint(y) = nilobj;
	    Child(t, Down(z));
	    /* actual(y) = CrossMake(whereto(z), t, GALL_PREC); */
	    actual(y) = CrossMake(whereto(z), t, type(y));
	    DisposeChild(Down(y));
	    CrossSequence(actual(y));
	    DisposeChild(NextDown(link));
	  }
	  else
	  {
	    /* galley was never attached, print message and kill it */
	    Error(22, 4, "galley %s deleted (never attached)",
	      WARN, &fpos(z), SymName(actual(z)));
	    debug1(DGF, D, "never-attached galley %s:", EchoFilePos(&fpos(z)));
	    ifdebug(DGF, D, DebugObject(z));
	    KillGalley(z, FALSE);
	    /* ***
	    link = NextDown(link);
	    *** */
	  }
	  break;


	case EXPAND_IND:
      
	  /* expand @HExpand or @VExpand to occupy everything possible */
	  dim = type(actual(y)) == HEXPAND ? COLM : ROWM;
	  Constrained(actual(y), &c, dim, &why);
	  if( constrained(c) )
	  { FULL_LENGTH b = back(actual(y), dim);
	    FULL_LENGTH f = fwd(actual(y), dim);
	    EnlargeToConstraint(&b, &f, &c);
	    debug1(DSA, D, "Promote %s AdjustSize", Image(type(actual(y))));
	    AdjustSize(actual(y), b, f, dim);
	  }
	  DisposeChild(NextDown(link));
	  break;


	case PAGE_LABEL_IND:

	  if( page_label != nilobj )
	  { DisposeObject(page_label);
	    page_label = nilobj;
	  }
	  Child(z, Down(y));
	  assert( type(z) == PAGE_LABEL, "Promote: type(z) != PAGE_LABEL!" );
	  assert( Down(z) != z, "Promote: PAGE_LABEL Down(z) == z!" );
	  Child(page_label, Down(z));
	  DeleteLink(Up(page_label));
	  DisposeChild(NextDown(link));
	  break;


	case CROSS_PREC:
	case CROSS_FOLL:
	case CROSS_FOLL_OR_PREC:
	case CROSS_TARG:
	      
	  debug2(DGS, DD, "root promote %s %s", Image(type(y)), EchoObject(y));
	  /* NB NO BREAK */


	case GALL_PREC:
	case GALL_FOLL:
	case GALL_FOLL_OR_PREC:
	case GALL_TARG:

	  CrossSequence(actual(y));
	  DisposeChild(NextDown(link));
	  break;


	case BEGIN_HEADER:
	case END_HEADER:
	case SET_HEADER:
	case CLEAR_HEADER:

	  Error(22, 10, "%s symbol ignored (out of place)", WARN, &fpos(y),
	    Image(type(y)));
	  DisposeChild(NextDown(link));
	  break;


	case WORD:
	case QWORD:
	case ONE_COL:
	case ONE_ROW:
	case WIDE:
	case HIGH:
	case HSHIFT:
	case VSHIFT:
	case HMIRROR:
	case VMIRROR:
	case HSCALE:
	case VSCALE:
	case HCOVER:
	case VCOVER:
	case HCONTRACT:
	case VCONTRACT:
	case HLIMITED:
	case VLIMITED:
	case HEXPAND:
	case VEXPAND:
	case START_HVSPAN:
	case START_HSPAN:
	case START_VSPAN:
	case HSPAN:
	case VSPAN:
	case ROTATE:
	case BACKGROUND:
	case SCALE:
	case KERN_SHRINK:
	case INCGRAPHIC:
	case SINCGRAPHIC:
	case PLAIN_GRAPHIC:
	case GRAPHIC:
	case LINK_SOURCE:
	case LINK_DEST:
	case LINK_URL:
	case ACAT:
	case HCAT:
	case ROW_THR:

	case CLOSURE:
	case NULL_CLOS:
	case LINK_DEST_NULL:
	case PAGE_LABEL:
	case CROSS:
	case FORCE_CROSS:

	  /* print this component */
	  debug0(DGS, DD, "root promote definite or indefinite");
	  if( !is_indefinite(type(y)) && size(y, ROWM) != 0 )
	  {
	    /* fix horizontally; work out which fonts needed */
	    SetLengthDim(COLM);
	    debug3(DGP,D, "  Promote calling FixAndPrint %s %s,%s", dimen(COLM),
	      EchoLength(back(y,COLM)), EchoLength(fwd(y, COLM)));
	    FixAndPrintObject(y, back(y, COLM), back(y, COLM), fwd(y, COLM),
	      COLM, FALSE, 0, 0, &aback, &afwd);

	    /* get label string from page_label, or prev_page_label + 1 */
	    if( page_label != nilobj && is_word(type(page_label)) )
	      label_string = string(page_label);
	    else if( prev_page_label!=nilobj && is_word(type(prev_page_label))
		&& sscanf( (char *) string(prev_page_label), "%d", &pnval)==1 )
	    {
	      sprintf( (char *) buff, "%d", pnval + 1);
	      label_string = buff;
	    }
	    else
	      label_string = AsciiToFull("?");

	    /* print prefatory or page separating material, including fonts */
	    debug1(DGS, DD, "root promote definite; label_string = %s",
	      label_string);
	    debug1(DCR, DD, "label_string = %s", label_string);
	    if( first )
	    { BackEnd->PrintBeforeFirstPage(size(hd, COLM), size(y, ROWM),
		label_string);
	      first = FALSE;
	    }
	    else
	      BackEnd->PrintBetweenPages(size(hd, COLM), size(y, ROWM),
		label_string);

	    /* dispose prev_page_label and move page_label to prev_page_label */
	    if( prev_page_label != nilobj )
	      DisposeObject(prev_page_label);
	    prev_page_label = page_label;
	    page_label = nilobj;

	    /* fix and print vertically */
	    debug1(DGF,D, "  Promote calling FixAndPrint %s", Image(type(y)));
	    debug3(DGP,D, "  Promote calling FixAndPrint %s %s,%s", dimen(ROWM),
	      EchoLength(back(y,ROWM)), EchoLength(fwd(y, ROWM)));
	    SetLengthDim(ROWM);
	    FixAndPrintObject(y, back(y,ROWM), back(y, ROWM), fwd(y, ROWM),
	      ROWM, FALSE, size(y, ROWM), 0, &aback, &afwd);

	  }
	  DisposeChild(NextDown(link));

	  /* scavenge any filter files now not needed */
	  FilterScavenge(FALSE);
	  break;


	case GAP_OBJ:

	  DisposeChild(NextDown(link));
	  break;


	default:
      
	  assert1(FALSE, "Promote:", Image(type(y)));
	  break;
	
      }
    }
    debug0(DGS, DD, "Promote returning (root galley).");
    return;
  }

  /* prepare the promotion */
  if( external_ver(dest) && gall_dir(hd) == ROWM )
  { if( threaded(dest) )
    { Parent(tmp1, UpDim(dest, COLM));
      assert( type(tmp1) == COL_THR, "Promote: tmp1 not COL_THR!" );
      y = FindSplitInGalley(hd);
      assert( type(y) == SPLIT, "Promote: FindSplitInGalley!" );
      Child(tmp2, DownDim(y, COLM));
      assert( type(tmp2) == COL_THR, "Promote: tmp2 not COL_THR!" );
      if( tmp1 != tmp2 )
      { FULL_LENGTH b = find_max(back(tmp1, COLM), back(tmp2, COLM));
	FULL_LENGTH f = find_max(fwd(tmp1, COLM),  fwd(tmp2, COLM));
	debug0(DSA, D, "calling AdjustSize(tmp1) from Promote (node merging)");
	AdjustSize(tmp1, b, f, COLM);
	debug0(DSA, D, "calling AdjustSize(tmp2) from Promote (node merging)");
	AdjustSize(tmp2, b, f, COLM);
	MergeNode(tmp1, tmp2);
      }
    }
    link = Up(dest_index);
  }
  else if( external_hor(dest) && gall_dir(hd) == COLM )
  { link = Up(dest_index);
  }
  else
  {
    dim = gall_dir(hd);
    for( link = hd;  NextDown(link) != stop_link;  )
    { Child(y, NextDown(link));
      debug1(DGS, DD, "ordinary promote examining %s", EchoObject(y));
      top_y = y;
      if( type(y) == SPLIT )
	Child(y, DownDim(y, dim));
      if( is_header(type(y)) )
      {
	assert(top_y == y, "Promote: header under SPLIT!");
	HandleHeader(hd, y);
      }
      else if( is_index(type(y)) )
	MoveLink(NextDown(link), Up(dest_index), PARENT);
      else link = NextDown(link);
    }
    assert( Down(hd) != stop_link, "Promote: Down(hd) == stop_link!" );
    assert( UpDim(dest, ROWM) == UpDim(dest, COLM), "Promote: dims!" );
    link = Up(dest);
  }
  
  /* promote components */
  TransferLinks(Down(hd), stop_link, link);

  debug0(DGS, DD, "] Promote returning; galley:");
  ifdebug(DGS, DD, DebugGalley(hd, nilobj, 2));
} /* end Promote */


/*@::MakeDead(), KillGalley()@************************************************/
/*                                                                           */
/*  static MakeDead(y)                                                       */
/*                                                                           */
/*  Convert object y into a DEAD object and remove it to the dead store.     */
/*                                                                           */
/*****************************************************************************/

static void MakeDead(OBJECT y)
{ static int	dead_count = 0;		/* number of DEAD objects seen       */
  static OBJECT	dead_store = nilobj;	/* where DEAD objects are kept       */

  debug1(DGS, DDD, "MakeDead( %s )", Image(type(y)));
  if( dead_store == nilobj )  New(dead_store, ACAT);
  type(y) = DEAD;
  MoveLink(Up(y), dead_store, PARENT);
  if( dead_count >= 150 )
  { DisposeChild(Down(dead_store));
  }
  else dead_count++;
  debug1(DGS, DDD, "MakeDead returning (dead_count = %d).", dead_count);
} /* end MakeDead */


/*****************************************************************************/
/*                                                                           */
/*  KillGalley(hd, optimize)                                                 */
/*                                                                           */
/*  Kill galley hd, which may be sized or unsized.  The index of hd must     */
/*  be UNATTACHED; it is moved out of its present location to a secret spot. */
/*                                                                           */
/*  If hd is to be optimized, generate all the stuff for the cross           */
/*  reference database.  However, don't do this if optimize is FALSE, for    */
/*  in that case hd is defective in some way and not optimizable.            */
/*                                                                           */
/*****************************************************************************/

void KillGalley(OBJECT hd, BOOLEAN optimize)
{ OBJECT prnt, link, y, z;
  debug2(DGF, D, "[ KillGalley(Galley %s into %s)",
	SymName(actual(hd)), SymName(whereto(hd)));
  assert( type(hd) == HEAD && Up(hd) != hd, "KillGalley: precondition!" );
  Parent(prnt, Up(hd));
  assert( type(prnt) == UNATTACHED, "KillGalley: UNATTACHED precondition!" );
  assert( Up(prnt) != prnt, "KillGalley: prnt!" );

  /* delete any ready_galls that might be hanging about */
  if( ready_galls(hd) != nilobj )
  { DisposeObject(ready_galls(hd));
    ready_galls(hd) = nilobj;
  }

  /* delete every remaining component */
  for( link = hd; NextDown(link) != hd; )
  { Child(y, NextDown(link));
    switch( type(y) )
    {
      case RECEIVING:	while( Down(y) != y )
			{ Child(z, Down(y));
			  DetachGalley(z);
			}
			DeleteNode(y);
			break;
		
      case RECEPTIVE:	assert( Down(y) == y, "KillGalley: RECEPTIVE!" );
			DeleteNode(y);
			break;

      case UNATTACHED:	assert( Down(y) != y, "KillGalley: UNATTACHED!" );
			Child(z, Down(y));  KillGalley(z, FALSE);
			break;

      case HEAD:	assert(FALSE, "KillGalley: head");
			break;

      default:		DisposeChild(NextDown(link));
			break;
    }
  }

  /* perform optimization calculations if required */
  if( optimize && opt_components(hd) != nilobj )
    CalculateOptimize(hd);

  /* move index into dead_store */
  MakeDead(prnt);
  debug0(DGF, D, "] KillGalley returning.");
} /* end KillGalley */


/*@::FreeGalley()@************************************************************/
/*                                                                           */
/*  FreeGalley(hd, stop_link, inners, relocate_link, sym)                    */
/*                                                                           */
/*  Free galley hd up to but not including stop_link.  *Inners is well-      */
/*  defined, either nilobj or an ACAT of galleys to be flushed.              */
/*                                                                           */
/*  Relocate_link defines what to do with any galley attached to one of the  */
/*  freed targets.  If it is non-nilobj, galley hd is searched onwards from  */
/*  it to see if a target can be found there.  If so, the galley is          */
/*  relocated to just before that point.  If not, or if relocate_link is     */
/*  nilobj, the galley is freed and added to *inners for flushing.  If the   */
/*  whereto() of such galley is sym, it is freed, not relocated, because the */
/*  cause of this call to FreeGalley is also targeted to sym, and it will    */
/*  consume all possible targets of sym.                                     */
/*                                                                           */
/*****************************************************************************/

void FreeGalley(OBJECT hd, OBJECT stop_link, OBJECT *inners,
OBJECT relocate_link, OBJECT sym)
{ OBJECT link, y, z, zlink, srch, index;
  assert( type(hd) == HEAD && sized(hd), "FreeGalley: pre!");
  assert( Up(hd) != hd, "FreeGalley: Up(hd)!" );
  assert( *inners == nilobj || type(*inners) == ACAT, "FreeGalley: ACAT!" );
  debug3(DGA, D, "[ FreeGalley(Galley %s into %s); rl %s nilobj",
    SymName(actual(hd)), SymName(whereto(hd)),
    relocate_link == nilobj ? "==" : "!=");

  /* close targets and move or flush any inner galleys */
  for( link = Down(hd);  link != stop_link;  link = NextDown(link) )
  { Child(y, link);
    if( type(y) == RECEIVING && actual(actual(y)) == InputSym )
      Error(22, 5, "forcing galley after input point", WARN, &fpos(actual(y)));
    else if( type(y) == RECEIVING )
    {
      /* either relocate or free each galley */
      for( zlink = Down(y);  zlink != y; )
      {	Child(z, zlink);
	zlink = NextDown(zlink);
	assert( type(z) == HEAD, "FreeGalley/RECEIVING: type(z) != HEAD!" );
	debug1(DGA, D, "FreeGalley examining galley %s", SymName(actual(z)));
	if( relocate_link != nilobj && whereto(z) != sym &&
	    (srch = SearchGalley(relocate_link, whereto(z), TRUE,
	    FALSE, TRUE, FALSE)) != nilobj )
	{
	  if( opt_components(z) != nilobj )  GazumpOptimize(z, actual(y));
	  debug2(DGA, D, "  FreeGalley relocating %s to just before %s",
	    SymName(actual(z)), SymName(whereto(z)));
	  DetachGalley(z);
	  Parent(index, Up(z));
	  MoveLink(Up(index), Up(srch), PARENT);  /* just before new dest */
	}
	else
	{ debug1(DGA, D, "  FreeGalley freeing galley %s", SymName(actual(z)));
	  FreeGalley(z, z, inners, nilobj, sym);
	  if( *inners == nilobj )  New(*inners, ACAT);
	  Link(*inners, y);
	}
      }
      non_blocking(y) = TRUE;
    }
    else if( type(y) == RECEPTIVE )
    { non_blocking(y) = TRUE;
    }
  }
  debug0(DGA, D, "] FreeGalley returning.");
} /* end FreeGalley */


/*@::SetTarget()@*************************************************************/
/*                                                                           */
/*  SetTarget(hd)                                                            */
/*                                                                           */
/*  Search for the target of unsized galley hd, and set the following:       */
/*                                                                           */
/*     whereto(hd)          The symbol which is this galley's target         */
/*     foll_or_prec(hd)     GALL_FOLL, GALL_PREC, GALL_FOLL_OR_PREC          */
/*     force_gall(hd)       TRUE is this is a forcing galley                 */
/*                                                                           */
/*****************************************************************************/

void SetTarget(OBJECT hd)
{ OBJECT x, y, link, cr, lpar, rpar, env;
  BOOLEAN copied;
  debug1(DGS, DD, "SetTarget(%s)", SymName(actual(hd)));
  assert( type(hd) == HEAD, "SetTarget: type(hd) != HEAD!" );
  Child(x, Down(hd));
  assert( type(x) == CLOSURE, "SetTarget: type(x) != CLOSURE!" );
  assert( has_target(actual(x)), "SetTarget: x has no target!" );

  /* search the parameters of x for @Target */
  cr = nilobj;
  for( link = Down(x);  link != x;  link = NextDown(link) )
  { Child(y, link);
    if( type(y) == PAR && is_target(actual(y)) )
    { assert( Down(y) != y, "SetTarget: Down(PAR)!" );
      Child(cr, Down(y));
      break;
    }
  }

  /* search the children of actual(x) for a default value of @Target */
  if( cr == nilobj )
  for( link = Down(actual(x));  link != actual(x);  link = NextDown(link) )
  { Child(y, link);
    if( is_target(y) )
    { cr = sym_body(y);
      break;
    }
  }
  assert(cr != nilobj, "SetTarget:  cr == nilobj!");
  
  /* if cr is not a cross-reference, expand it until it is */
  copied = FALSE;
  if( !is_cross(type(cr)) )
  { OBJECT nbt[2], nft[2], ntarget, ncrs, nenclose;
    nbt[COLM] = nft[COLM] = nbt[ROWM] = nft[ROWM] = nilobj;
    ntarget = ncrs = nenclose = nilobj;
    cr = CopyObject(cr, &fpos(x));
    copied = TRUE;
    env = GetEnv(x);
    cr = Manifest(cr, env, &InitialStyle, nbt, nft, &ntarget, &ncrs,
           FALSE, FALSE, &nenclose, TRUE);
  }

  /* check that cr is now a cross-reference object */
  debug1(DGS, DD, "SetTarget examining %s", EchoObject(cr));
  debug1(DGS, DD, "  type(cr) = %s", Image( (int) type(cr)) );
  if( !is_cross(type(cr)) )
    Error(22, 6, "target of %s is not a cross reference",
      FATAL, &fpos(cr), SymName(actual(x)));

  /* determine which symbol is the target of this galley */
  Child(lpar, Down(cr));
  if( type(lpar) != CLOSURE )
    Error(22, 7, "left parameter of %s is not a symbol",
      FATAL, &fpos(lpar), KW_CROSS);
  whereto(hd) = actual(lpar);

  /* determine the direction from the right parameter */
  Child(rpar, NextDown(Down(cr)));
  if( !is_word(type(rpar)) )
  {
    Error(22, 8, "replacing %s%s? by %s%s%s", WARN, &fpos(rpar),
      SymName(actual(lpar)), KW_CROSS,
      SymName(actual(lpar)), KW_CROSS, KW_FOLLOWING);
    foll_or_prec(hd) = GALL_FOLL;
  }
  else if( StringEqual(string(rpar), KW_PRECEDING) )
     foll_or_prec(hd) = GALL_PREC;
  else if( StringEqual(string(rpar), KW_FOLLOWING) )
     foll_or_prec(hd) = GALL_FOLL;
  else if( StringEqual(string(rpar), KW_FOLL_OR_PREC) )
     foll_or_prec(hd) = GALL_FOLL_OR_PREC;
  else
  {
    Error(22, 9, "replacing %s%s%s by %s%s%s",
      WARN, &fpos(rpar), SymName(actual(lpar)), KW_CROSS,
      string(rpar), SymName(actual(lpar)), KW_CROSS, KW_FOLLOWING);
    foll_or_prec(hd) = GALL_FOLL;
  }

  /* determine whether this is a forcing galley */
  force_gall(hd) = force_target(actual(hd)) || type(cr) == FORCE_CROSS;

  if( copied )  DisposeObject(cr);
} /* end SetTarget */


/*@::CheckComponentOrder()@***************************************************/
/*                                                                           */
/*  int CheckComponentOrder(preceder, follower)                              */
/*                                                                           */
/*  Check the ordering relation between components preceder and follower,    */
/*  and return its current status:                                           */
/*                                                                           */
/*      CLEAR     follower definitely follows preceder, and always will;     */
/*      PROMOTE   follower is not prevented from following preceder;         */
/*      CLOSE     follower must move down its galley to follow preceder;     */
/*      BLOCK     follower cannot be guaranteed to follow preceder.          */
/*                                                                           */
/*****************************************************************************/

int CheckComponentOrder(OBJECT preceder, OBJECT follower)
{ OBJECT prec_galley, foll_galley, z;  int res;
  debug2(DGS, DD, "CheckComponentOrder( %s, %s )",
    EchoObject(preceder), EchoObject(follower));
  Parent(prec_galley, Up(preceder));
  Parent(foll_galley, Up(follower));
  if( prec_galley == foll_galley )
  { res = CLOSE;
    for( z = Up(follower);  z != foll_galley;  z = pred(z, CHILD) )
    if( z == Up(preceder) )
    { res = CLEAR;
      break;
    }
  }
  else
  { res = PROMOTE;
    while( Up(prec_galley) != prec_galley )
    { Parent(z, Up(prec_galley));	/* index of galley */
      Parent(prec_galley, Up(z));	/* enclosing galley */
      if( prec_galley == foll_galley )
      {	res = BLOCK;
	break;
      }
    }
  }
  debug1(DGS, DD, "CheckComponentOrder returning %s", Image(res));
  return res;
} /* end CheckComponentOrder */