1 /*@z19.c:Galley Attaching:DetachGalley()@*************************************/
2 /*                                                                           */
3 /*  THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.39)                       */
4 /*  COPYRIGHT (C) 1991, 2008 Jeffrey H. Kingston                             */
5 /*                                                                           */
6 /*  Jeffrey H. Kingston (jeff@it.usyd.edu.au)                                */
7 /*  School of Information Technologies                                       */
8 /*  The University of Sydney 2006                                            */
9 /*  AUSTRALIA                                                                */
10 /*                                                                           */
11 /*  This program is free software; you can redistribute it and/or modify     */
12 /*  it under the terms of the GNU General Public License as published by     */
13 /*  the Free Software Foundation; either Version 3, or (at your option)      */
14 /*  any later version.                                                       */
15 /*                                                                           */
16 /*  This program is distributed in the hope that it will be useful,          */
17 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
18 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
19 /*  GNU General Public License for more details.                             */
20 /*                                                                           */
21 /*  You should have received a copy of the GNU General Public License        */
22 /*  along with this program; if not, write to the Free Software              */
23 /*  Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA   */
24 /*                                                                           */
25 /*  FILE:         z19.c                                                      */
26 /*  MODULE:       Galley Attaching                                           */
27 /*  EXTERNS:      SearchGalley(), AttachGalley(), DetachGalley()             */
28 /*                                                                           */
29 /*****************************************************************************/
30 #include "externs.h"
31 
32 
33 /*****************************************************************************/
34 /*                                                                           */
35 /*  OBJECT InterposeScale(y, scale_factor, dim)                              */
36 /*                                                                           */
37 /*  Interpose a @Scale symbol above y with the given scale factor.           */
38 /*                                                                           */
39 /*****************************************************************************/
40 
InterposeScale(OBJECT y,int scale_factor,int dim)41 static OBJECT InterposeScale(OBJECT y, int scale_factor, int dim)
42 { OBJECT res;
43   New(res, SCALE);
44   FposCopy(fpos(res), fpos(y));
45   if( dim == COLM )
46   { bc(constraint(res)) = scale_factor;
47     fc(constraint(res)) = 1 * SF;
48   }
49   else
50   { bc(constraint(res)) = 1 * SF;
51     fc(constraint(res)) = scale_factor;
52   }
53   back(res, dim) = (back(y, dim) * scale_factor) / SF;
54   fwd(res, dim)  = (fwd(y, dim) * scale_factor) / SF;
55   back(res, 1-dim) = back(y, 1-dim);
56   fwd(res, 1-dim)  = fwd(y, 1-dim);
57   ReplaceNode(res, y);
58   Link(res, y);
59   return res;
60 } /* end InterposeScale */
61 
62 
63 /*****************************************************************************/
64 /*                                                                           */
65 /*  OBJECT InterposeWideOrHigh(y, dim)                                       */
66 /*                                                                           */
67 /*  Interpose a @Wide or @High symbol above y with the same size as y, with  */
68 /*  a value which prevents any further increase in the size of y.            */
69 /*                                                                           */
70 /*****************************************************************************/
71 
InterposeWideOrHigh(OBJECT y,int dim)72 static OBJECT InterposeWideOrHigh(OBJECT y, int dim)
73 { OBJECT res;
74   New(res, dim == COLM ? WIDE : HIGH);
75   FposCopy(fpos(res), fpos(y));
76   back(res, dim) = back(y, dim);
77   fwd(res, dim)  = fwd(y, dim);
78   back(res, 1-dim) = back(y, 1-dim);
79   fwd(res, 1-dim)  = fwd(y, 1-dim);
80   SetConstraint(constraint(res), MAX_FULL_LENGTH, size(res, dim), MAX_FULL_LENGTH);
81   ReplaceNode(res, y);
82   Link(res, y);
83   return res;
84 } /* end InterposeWideOrHigh */
85 
86 
87 /*****************************************************************************/
88 /*                                                                           */
89 /*  DetachGalley(hd)                                                         */
90 /*                                                                           */
91 /*  Detach galley hd from its target.                                        */
92 /*                                                                           */
93 /*****************************************************************************/
94 
DetachGalley(OBJECT hd)95 void DetachGalley(OBJECT hd)
96 { OBJECT prnt, index;
97   assert( type(hd) == HEAD && Up(hd) != hd, "DetachGalley: precondition!" );
98   debug1(DGA, D, "DetachGalley( %s )", SymName(actual(hd)));
99   Parent(prnt, Up(hd));
100   assert( Up(prnt) != prnt, "DetachGalley: parent!" );
101   New(index, UNATTACHED);
102   actual(index) = nilobj;
103   non_blocking(index) = TRUE;
104   blocked(index) = FALSE;
105   pinpoint(index) = nilobj;
106   MoveLink(Up(hd), index, PARENT);
107   Link(NextDown(Up(prnt)), index);
108   debug0(DGA, D, "DetachGalley returning.");
109 } /* end DetachGalley */
110 
111 
112 /*@::SearchGalley()@**********************************************************/
113 /*                                                                           */
114 /*  OBJECT SearchGalley(start, sym, forwards, subgalleys, closures, input)   */
115 /*                                                                           */
116 /*  Search a galley and its sub-galleys for a target which uses sym.  The    */
117 /*  meanings of the flags are as follows:                                    */
118 /*                                                                           */
119 /*    forwards     If TRUE, search forwards from just after start, else      */
120 /*                 search backwards from just before start                   */
121 /*    subgalleys   If TRUE, search down into sub-galleys of this galley      */
122 /*    closures     If TRUE, closures in this galley are acceptable results   */
123 /*    input        If TRUE, InputSym is an acceptable result                 */
124 /*                                                                           */
125 /*****************************************************************************/
126 
SearchGalley(OBJECT start,OBJECT sym,BOOLEAN forwards,BOOLEAN subgalleys,BOOLEAN closures,BOOLEAN input)127 OBJECT SearchGalley(OBJECT start, OBJECT sym, BOOLEAN forwards,
128 BOOLEAN subgalleys, BOOLEAN closures, BOOLEAN input)
129 { OBJECT y, res, z, zlink, link;
130   ifdebug(DGA, D, Parent(y, start));
131   debug6(DGA, D, "[ SearchGalley(%s, %s, %s, %s, %s, %s)",
132         type(start) == HEAD ? (char *) SymName(actual(start)) :
133         type(y) == HEAD ? (char *) SymName(actual(y)) : "link",
134 	SymName(sym),
135 	forwards ? "fwd" : "back", subgalleys ? "subgalleys" : "nosubgalleys",
136 	closures ? "closures" : "noclosures", input ? "input" : "noinput");
137   assert( type(start) == LINK || type(start) == HEAD, "SearchGalley: start!" );
138 
139   link = forwards ? NextDown(start) : PrevDown(start);
140   res = nilobj;
141   while( res == nilobj && type(link) != HEAD )
142   { Child(y, link);
143     switch( type(y) )
144     {
145       case UNATTACHED:
146       case RECEIVING:
147 
148         debug1(DGA, D, "  examining %s", EchoIndex(y));
149 	if( subgalleys )
150 	for( zlink = Down(y); zlink!=y && res==nilobj; zlink=NextDown(zlink) )
151 	{ Child(z, zlink);
152 	  res = SearchGalley(z, sym, TRUE, TRUE, TRUE, input);
153 	}
154 	if( res == nilobj && input && type(y) == RECEIVING &&
155 	    actual(actual(y)) == InputSym )
156 	  res = y;
157 	break;
158 
159 
160       case RECEPTIVE:
161 
162         debug1(DGA, D, "  examining %s", EchoIndex(y));
163 	if( closures && type(actual(y)) == CLOSURE
164 		     && SearchUses(actual(actual(y)), sym) )  res = y;
165 	else if( input && actual(actual(y)) == InputSym )  res = y;
166 	break;
167 
168 
169       default:
170 
171 	break;
172 
173     }
174     link = forwards ? NextDown(link) : PrevDown(link);
175   }
176   debug1(DGA, D, "] SearchGalley returning %s", EchoIndex(res));
177   return res;
178 } /* end SearchGalley */
179 
180 
181 /*@@**************************************************************************/
182 /*                                                                           */
183 /*  int AttachGalley(hd, inners, suspend_pt)                                 */
184 /*                                                                           */
185 /*  Attach galley hd, which may be unsized, to a destination.  This involves */
186 /*  searching for a destination forward or back from the attachment point of */
187 /*  hd and promoting up to and including the first definite component of hd. */
188 /*                                                                           */
189 /*  Although AttachGalley never flushes any galleys, it may identify some    */
190 /*  galleys which should be flushed, even if the attach is itself not        */
191 /*  successful.  These are returned in *inners, or nilobj if none.           */
192 /*                                                                           */
193 /*  The integer returned by AttachGalley indicates what happened to hd:      */
194 /*                                                                           */
195 /*    ATTACH_KILLED     The galley was sized to begin with but no target     */
196 /*                      for it could be found.  The galley has been killed   */
197 /*                      and that's the end of it.                            */
198 /*                                                                           */
199 /*    ATTACH_INPUT      When searching for a target for the galley we came   */
200 /*                      upon InputSym, suggesting that the target might be   */
201 /*                      still to be read.  So the galley has been linked to  */
202 /*                      that InputSym and must now wait.                     */
203 /*                                                                           */
204 /*    ATTACH_NOTARGET   The galley is unsized and no target could be found   */
205 /*                      for it.  This is fine, it just means that we can't   */
206 /*                      flush the galley now and we must try again later.    */
207 /*                                                                           */
208 /*    ATTACH_SUSPEND    The galley is sized and a target was found for it,   */
209 /*                      but the first component of the galley proved to be   */
210 /*                      indefinite so could not be promoted.  The galley     */
211 /*                      remains unattached but is moved to just before its   */
212 /*                      target so that it can find it easily later when its  */
213 /*                      first component becomes definite and it is flushed.  */
214 /*                                                                           */
215 /*    ATTACH_NULL       The galley is sized and a target was found for it,   */
216 /*                      but the body of the galley proved to be null (i.e.   */
217 /*                      there were no definite components to be flushed).    */
218 /*                      This is to be treated just like the normal case      */
219 /*                      following, except that the target is replaced by     */
220 /*                      @Null rather than by its body.                       */
221 /*                                                                           */
222 /*    ATTACH_ACCEPT     The galley is sized and a target was found for it,   */
223 /*                      and one component of the galley has been promoted.   */
224 /*                                                                           */
225 /*****************************************************************************/
226 
AttachGalley(OBJECT hd,OBJECT * inners,OBJECT * suspend_pt)227 int AttachGalley(OBJECT hd, OBJECT *inners, OBJECT *suspend_pt)
228 { OBJECT hd_index;		/* the index of hd in the enclosing galley   */
229   OBJECT hd_inners;		/* inner galleys of hd, if unsized           */
230   OBJECT dest;			/* the target @Galley hd empties into        */
231   OBJECT dest_index;		/* the index of dest                         */
232   OBJECT target;		/* the target indefinite containing dest     */
233   OBJECT target_index;		/* the index of target                       */
234   OBJECT target_galley;		/* the body of target, made into a galley    */
235   OBJECT tg_inners;		/* inner galleys of target_galley            */
236   BOOLEAN need_precedes = FALSE;/* true if destination lies before galley    */
237   OBJECT recs;			/* list of recursive definite objects        */
238   OBJECT link, y = nilobj;	/* for scanning through the components of hd */
239   CONSTRAINT c;			/* temporary variable holding a constraint   */
240   OBJECT env, n1, tmp, zlink, z, sym;	/* placeholders and temporaries	     */
241   BOOLEAN was_sized;		/* true if sized(hd) initially               */
242   int dim;			/* the galley direction                      */
243   FULL_LENGTH perp_back, perp_fwd;
244   OBJECT why, junk;
245 
246   debug2(DGA, D, "[ AttachGalley(Galley %s into %s)",
247 	SymName(actual(hd)), SymName(whereto(hd)));
248   ifdebug(DGA, DD, DebugGalley(hd, nilobj, 4));
249   assert( Up(hd) != hd, "AttachGalley: no index!" );
250   Parent(hd_index, Up(hd));
251   assert( type(hd_index) == UNATTACHED, "AttachGalley: not UNATTACHED!" );
252   hd_inners = tg_inners = nilobj;
253   was_sized = sized(hd);
254   dim = gall_dir(hd);
255 
256   for(;;)
257   {
258     /*************************************************************************/
259     /*                                                                       */
260     /*  Search for a destination for hd.  If hd is unsized, search for       */
261     /*  inner galleys preceding it first of all, then for receptive objects  */
262     /*  following it, possibly in inner galleys.  If no luck, exit.          */
263     /*  If hd is sized, search only for receptive objects in the current     */
264     /*  galley below the current spot, and fail if cannot find any.          */
265     /*                                                                       */
266     /*************************************************************************/
267 
268     sym = whereto(hd);
269     if( sized(hd) )
270     {
271       /* sized galley case: search on from current spot */
272       target_index = SearchGalley(Up(hd_index), sym, TRUE, FALSE, TRUE, TRUE);
273       if( target_index == nilobj )
274       {
275 	/* search failed to find any new target, so kill the galley */
276 	for( link = Down(hd); link != hd; link = NextDown(link) )
277 	{ Child(y, link);
278 	  if( type(y) == SPLIT )  Child(y, DownDim(y, dim));
279 	  if( is_definite(type(y)) )  break;
280 	}
281 	if( link != hd )
282 	  Error(19, 1, "galley %s deleted from here (no target)",
283 	    WARN, &fpos(y), SymName(actual(hd)));
284 	if( hd_inners != nilobj )  DisposeObject(hd_inners), hd_inners=nilobj;
285 	if( tg_inners != nilobj )  DisposeObject(tg_inners), tg_inners=nilobj;
286 	KillGalley(hd, FALSE);
287 	*inners = nilobj;
288 	debug0(DGA, D, "] AttachGalley returning ATTACH_KILLED");
289 	return ATTACH_KILLED;
290       }
291       else if( actual(actual(target_index)) == InputSym )
292       {
293 	/* search found input object, so suspend on that */
294 	DeleteNode(hd_index);
295 	Link(target_index, hd);
296 	*inners = nilobj;
297 	debug0(DGA, D, "] AttachGalley returning ATTACH_INPUT");
298 	return ATTACH_INPUT;
299       }
300 
301     }
302     else /* unsized galley, either backwards or normal */
303     {
304       if( foll_or_prec(hd) == GALL_PREC )
305       {	target_index= SearchGalley(Up(hd_index), sym, FALSE, TRUE,TRUE,FALSE);
306 	need_precedes = FALSE;
307       }
308       else
309       {	target_index = SearchGalley(Up(hd_index), sym, FALSE,TRUE,FALSE,FALSE);
310 	need_precedes = (target_index != nilobj);
311 	if( target_index == nilobj )
312 	  target_index = SearchGalley(Up(hd_index), sym, TRUE,TRUE,TRUE,FALSE);
313       }
314 
315       /* if no luck, exit without error */
316       if( target_index == nilobj )
317       {	*inners = nilobj;
318 	debug0(DGA, D, "] AttachGalley returning ATTACH_NOTARGET");
319 	return ATTACH_NOTARGET;
320       }
321     }
322     assert( type(target_index) == RECEPTIVE, "AttachGalley: target_index!" );
323     target = actual(target_index);
324     assert( type(target) == CLOSURE, "AttachGalley: target!" );
325 
326     /* set target_galley to the expanded value of target */
327     debug1(DYY, D, "[ EnterErrorBlock(FALSE) (expanding target %s)",
328       SymName(actual(target)));
329     EnterErrorBlock(FALSE);
330     New(target_galley, HEAD);
331     force_gall(target_galley) = FALSE;
332     actual(target_galley) = actual(target);
333     enclose_obj(target_galley) = limiter(target_galley) = nilobj;
334     ClearHeaders(target_galley);
335     opt_components(target_galley) = opt_constraints(target_galley) = nilobj;
336     gall_dir(target_galley) = external_hor(target) ? COLM : ROWM;
337     FposCopy(fpos(target_galley), fpos(target));
338     whereto(target_galley) = ready_galls(target_galley) = nilobj;
339     foll_or_prec(target_galley) = GALL_FOLL;
340     must_expand(target_galley) = FALSE;
341     sized(target_galley) = FALSE;
342     seen_nojoin(target_galley) = FALSE;
343 
344     /* get perpendicular constraint (none if horizontal galley) */
345     if( dim == ROWM )
346     {
347       Constrained(target, &c, 1-dim, &junk);
348       if( !constrained(c) )
349         Error(19, 2, "receptive symbol %s has unconstrained width",
350 	  FATAL, &fpos(target), SymName(actual(target)));
351       debug2(DSC, DD, "Constrained( %s, 1-dim ) = %s",
352 	EchoObject(target), EchoConstraint(&c));
353       if( !FitsConstraint(0, 0, c) )
354       { debug0(DGA, D, "  reject: target_galley horizontal constraint is -1");
355 	y = nilobj;
356         goto REJECT;
357       }
358     }
359     else /* actually unused */
360       SetConstraint(c, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
361 
362     debug1(DGA, DDD, "  expanding %s", EchoObject(target));
363     tmp = CopyObject(target, no_fpos);
364     Link(target_galley, tmp);
365     env = DetachEnv(tmp);
366     debug4(DGM, D, "  external_ver(%s) = %s, external_hor(%s) = %s",
367       SymName(actual(target)), bool(external_ver(target)),
368       SymName(actual(target)), bool(external_hor(target)));
369     SizeGalley(target_galley, env,
370 	external_ver(target) || external_hor(target),
371 	threaded(target), non_blocking(target_index),
372 	trigger_externs(target_index), &save_style(target),
373 	&c, whereto(hd), &dest_index, &recs, &tg_inners,
374 	enclose_obj(hd) != nilobj ? CopyObject(enclose_obj(hd), no_fpos):nilobj);
375     debug1(DGA, D, "  AttachGalley tg_inners: %s", DebugInnersNames(tg_inners));
376     if( recs != nilobj )  ExpandRecursives(recs);
377     dest = actual(dest_index);
378     if( underline(dest) == UNDER_UNDEF )  underline(dest) = UNDER_OFF;
379 
380     /* verify that hd satisfies any horizontal constraint on dest */
381     if( dim == ROWM )
382     {
383       debug1(DGA, DDD, "  checking hor fit of hd in %s",SymName(actual(dest)));
384       Constrained(dest, &c, 1-dim, &junk);
385       debug3(DSC, DD, "Constrained( %s, %s ) = %s",
386 	EchoObject(dest), dimen(1-dim), EchoConstraint(&c));
387       assert( constrained(c), "AttachGalley: dest unconstrained!" );
388       if( !FitsConstraint(0, 0, c) )
389       { debug0(DGA, D, "  reject: hd horizontal constraint is -1");
390 	y = nilobj;
391         goto REJECT;
392       }
393     }
394 
395     /* manifest and size the galley if not done yet */
396     if( !sized(hd) )
397     {
398       debug2(DYY, D, "[ EnterErrorBlock(TRUE) (sizing galley %s into %s)",
399 	SymName(actual(hd)), SymName(whereto(hd)));
400       EnterErrorBlock(TRUE);
401       n1 = nilobj;
402       Child(y, Down(hd));
403       env = DetachEnv(y);
404       /*** threaded() only defined in ROWM case
405       SizeGalley(hd, env, TRUE, threaded(dest), non_blocking(target_index),
406 	TRUE, &save_style(dest), &c, nilobj, &n1, &recs, &hd_inners);
407       *** */
408       SizeGalley(hd, env, TRUE, dim == ROWM ? threaded(dest) : FALSE,
409 	non_blocking(target_index), TRUE, &save_style(dest), &c, nilobj,
410 	&n1, &recs, &hd_inners, nilobj);
411       debug1(DGA, D,"  AttachGalley hd_inners: %s",DebugInnersNames(hd_inners));
412       if( recs != nilobj )  ExpandRecursives(recs);
413       if( need_precedes )		/* need an ordering constraint */
414       {	OBJECT index1, index2;
415         New(index1, PRECEDES);
416 	New(index2, FOLLOWS);
417 	blocked(index2) = FALSE;
418 	tmp = MakeWord(WORD, STR_EMPTY, no_fpos);
419 	Link(index1, tmp);  Link(index2, tmp);
420 	Link(Up(hd_index), index1);
421 	Link(Down(hd), index2);
422 	debug0(DGA, D, "  inserting PRECEDES and FOLLOWS");
423       }
424       LeaveErrorBlock(TRUE);
425       debug0(DYY, D, "] LeaveErrorBlock(TRUE) (finished sizing galley)");
426     }
427 
428     if( dim == ROWM )
429     { if( !FitsConstraint(back(hd, 1-dim), fwd(hd, 1-dim), c) )
430       { debug3(DGA, D, "  reject: hd %s,%s does not fit target_galley %s",
431 	  EchoLength(back(hd, 1-dim)), EchoLength(fwd(hd, 1-dim)),
432 	  EchoConstraint(&c));
433         Error(19, 3, "too little horizontal space for galley %s at %s",
434 	  WARN, &fpos(hd), SymName(actual(hd)), SymName(actual(dest)));
435         goto REJECT;
436       }
437     }
438 
439     /* check status of first component of hd */
440     debug0(DGA, DDD, "  now ready to attach; hd =");
441     ifdebug(DGA, DDD, DebugObject(hd));
442     for( link = Down(hd);  link != hd;  link = NextDown(link) )
443     {
444       Child(y, link);
445       debug1(DGA, DDD, "  examining %s", EchoIndex(y));
446       if( type(y) == SPLIT )  Child(y, DownDim(y, dim));
447       switch( type(y) )
448       {
449 
450 	case EXPAND_IND:
451 	case SCALE_IND:
452 	case COVER_IND:
453 	case GALL_PREC:
454 	case GALL_FOLL:
455 	case GALL_FOLL_OR_PREC:
456 	case GALL_TARG:
457 	case CROSS_PREC:
458 	case CROSS_FOLL:
459 	case CROSS_FOLL_OR_PREC:
460 	case CROSS_TARG:
461 	case PAGE_LABEL_IND:
462 
463 	  break;
464 
465 
466 	case PRECEDES:
467 	case UNATTACHED:
468 
469 	  if( was_sized )
470 	  { /* SizeGalley was not called, so hd_inners was not set by it */
471 	    if( hd_inners == nilobj )  New(hd_inners, ACAT);
472 	    Link(hd_inners, y);
473 	  }
474 	  break;
475 
476 
477 	case RECEPTIVE:
478 
479 	  goto SUSPEND;
480 
481 
482 	case RECEIVING:
483 
484 	  goto SUSPEND;
485 
486 
487 	case FOLLOWS:
488 
489 	  Child(tmp, Down(y));
490 	  if( Up(tmp) == LastUp(tmp) )
491 	  { link = pred(link, CHILD);
492 	    debug0(DGA, DD, "  disposing FOLLOWS");
493 	    DisposeChild(NextDown(link));
494 	    break;
495 	  }
496 	  Parent(tmp, Up(tmp));
497 	  assert(type(tmp) == PRECEDES, "Attach: PRECEDES!");
498 	  switch( CheckComponentOrder(tmp, target_index) )
499 	  {
500 	    case CLEAR:		DeleteNode(tmp);
501 				link = pred(link, CHILD);
502 				DisposeChild(NextDown(link));
503 				break;
504 
505 	    case PROMOTE:	break;
506 
507 	    case BLOCK:		debug0(DGA, DD, "CheckContraint: BLOCK");
508 				goto SUSPEND;
509 
510 	    case CLOSE:		debug0(DGA, D, "  reject: CheckContraint");
511 				goto REJECT;
512 	  }
513 	  break;
514 
515 
516 	case GAP_OBJ:
517 
518 	  underline(y) = underline(dest);
519 	  if( !join(gap(y)) )  seen_nojoin(hd) = TRUE;
520 	  break;
521 
522 
523 	case BEGIN_HEADER:
524 	case END_HEADER:
525 	case SET_HEADER:
526 	case CLEAR_HEADER:
527 
528 	  /* do nothing until actually promoted out of here */
529 	  underline(y) = underline(dest);
530 	  break;
531 
532 
533 	case CLOSURE:
534 	case CROSS:
535 	case FORCE_CROSS:
536 	case NULL_CLOS:
537 	case PAGE_LABEL:
538 
539 	  underline(y) = underline(dest);
540 	  break;
541 
542 
543 	case WORD:
544 	case QWORD:
545 	case ONE_COL:
546 	case ONE_ROW:
547 	case WIDE:
548 	case HIGH:
549 	case HSHIFT:
550 	case VSHIFT:
551 	case HMIRROR:
552 	case VMIRROR:
553 	case HSCALE:
554 	case VSCALE:
555 	case HCOVER:
556 	case VCOVER:
557 	case HCONTRACT:
558 	case VCONTRACT:
559 	case HLIMITED:
560 	case VLIMITED:
561 	case HEXPAND:
562 	case VEXPAND:
563 	case START_HVSPAN:
564 	case START_HSPAN:
565 	case START_VSPAN:
566 	case HSPAN:
567 	case VSPAN:
568 	case ROTATE:
569 	case BACKGROUND:
570 	case SCALE:
571 	case KERN_SHRINK:
572 	case INCGRAPHIC:
573 	case SINCGRAPHIC:
574 	case PLAIN_GRAPHIC:
575 	case GRAPHIC:
576 	case LINK_SOURCE:
577 	case LINK_DEST:
578 	case LINK_DEST_NULL:
579 	case LINK_URL:
580 	case ACAT:
581 	case HCAT:
582 	case VCAT:
583 	case ROW_THR:
584 	case COL_THR:
585 
586 
587 	  underline(y) = underline(dest);
588 	  if( dim == ROWM )
589 	  {
590 	    /* make sure y is not joined to a target below (vertical only) */
591 	    for( zlink = NextDown(link); zlink != hd; zlink = NextDown(zlink) )
592 	    { Child(z, zlink);
593 	      switch( type(z) )
594 	      {
595 	        case RECEPTIVE:
596 
597 		  if( non_blocking(z) )
598 		  { zlink = PrevDown(zlink);
599 		    DeleteNode(z);
600 		  }
601 		  else
602 		  { y = z;
603 		    goto SUSPEND;
604 		  }
605 		  break;
606 
607 
608 	        case RECEIVING:
609 
610 		  if( non_blocking(z) )
611 		  { zlink = PrevDown(zlink);
612 		    while( Down(z) != z )
613 		    { Child(tmp, Down(y));
614 		      if( opt_components(tmp) != nilobj )
615 		      { DisposeObject(opt_components(tmp));
616 		        opt_components(tmp) = nilobj;
617 		        debug3(DOG, D, "AttachGalley(%s) de-optimizing %s %s",
618 			  SymName(actual(hd)), SymName(actual(tmp)), "(join)");
619 		      }
620 		      DetachGalley(tmp);
621 		      KillGalley(tmp, FALSE);
622 		    }
623 		    DeleteNode(z);
624 		  }
625 		  else
626 		  { y = z;
627 		    goto SUSPEND;
628 		  }
629 		  break;
630 
631 
632 	        case GAP_OBJ:
633 
634 		  if( !join(gap(z)) )  zlink = PrevDown(hd);
635 		  break;
636 
637 
638 	        default:	break;
639 	      }
640 	    }
641 
642 	    /* if HCAT, try vertical hyphenation (vertical galleys only) */
643 	    if( type(y) == HCAT )  VerticalHyphenate(y);
644 	  }
645 
646 
647 	  /* check availability of parallel space for the first component */
648 	  why = nilobj;
649 	  Constrained(dest, &c, dim, &why);
650 	  debug3(DGF, DD, "  dest parallel Constrained(%s, %s) = %s",
651 	    EchoObject(dest), dimen(dim), EchoConstraint(&c));
652 	  if( !FitsConstraint(back(y, dim), fwd(y, dim), c) )
653 	  { BOOLEAN scaled;
654 
655 	    /* if forcing galley doesn't fit, try scaling first component */
656 	    scaled = FALSE;
657 	    if( force_gall(hd) && size(y, dim) > 0 )
658 	    { int scale_factor;
659 	      scale_factor = ScaleToConstraint(back(y,dim), fwd(y,dim), &c);
660 	      if( scale_factor > 0.5 * SF )
661 	      {	char num1[20], num2[20];
662 		sprintf(num1, "%.1fc", (float) size(y, dim) / CM);
663 		sprintf(num2, "%.1fc", (float) bfc(c) / CM);
664 		if( dim == ROWM )
665 		  Error(19, 4, "%s object too high for %s space; %s inserted",
666 		    WARN, &fpos(y), num1, num2, KW_SCALE);
667 		else
668 		  Error(19, 5, "%s object too wide for %s space; %s inserted",
669 		    WARN, &fpos(y), num1, num2, KW_SCALE);
670 		y = InterposeScale(y, scale_factor, dim);
671 		scaled = TRUE;
672 	      }
673 	    }
674 
675 	    /* otherwise we must reject, and warn the user */
676 	    if( !scaled )
677 	    { char num1[20], num2[20];
678 	      debug3(DGA, D, "  reject: vsize %s,%s in %s; y=",
679 		EchoLength(back(y, dim)), EchoLength(fwd(y, dim)),
680 		EchoConstraint(&c));
681 	      ifdebug(DGA, DD, DebugObject(y));
682 	      if( size(y, dim) > 0 )
683 	      { sprintf(num1, "%.1fc", (float) size(y, dim) / CM);
684 	        sprintf(num2, "%.1fc", (float) bfc(c) / CM);
685 	        if( dim == ROWM )
686 		  Error(19, 12, "%s object too high for %s space; will try elsewhere",
687 		    WARN, &fpos(y), num1, num2);
688 	        else
689 		  Error(19, 13, "%s object too wide for %s space; will try elsewhere",
690 		    WARN, &fpos(y), num1, num2);
691 	      }
692 	      goto REJECT;
693 	    }
694 
695 	  }
696 
697 	  /* check availability of perpendicular space for first component */
698 	  if( dim == ROWM )
699 	  { perp_back = back(hd, 1-dim);  perp_fwd = fwd(hd, 1-dim);
700 	  }
701 	  else
702 	  { perp_back = back(y, 1-dim);  perp_fwd = fwd(y, 1-dim);
703 	  }
704 	  Constrained(dest, &c, 1-dim, &junk);
705 	  debug3(DGF, DD, "  dest perpendicular Constrained(%s, %s) = %s",
706 	    EchoObject(dest), dimen(1-dim), EchoConstraint(&c));
707 	  if( !FitsConstraint(perp_back, perp_fwd, c) )
708 	  { BOOLEAN scaled;
709 
710 	    /* if forcing galley doesn't fit, try scaling first component */
711 	    scaled = FALSE;
712 	    if( force_gall(hd) && perp_back + perp_fwd > 0 )
713 	    { int scale_factor;
714 	      scale_factor = ScaleToConstraint(perp_back, perp_fwd, &c);
715 	      if( scale_factor > 0.5 * SF )
716 	      {	char num1[20], num2[20];
717 		sprintf(num1, "%.1fc", (float) (perp_back + perp_fwd) / CM);
718 		sprintf(num2, "%.1fc", (float) bfc(c) / CM);
719 		if( 1-dim == ROWM )
720 		  Error(19, 6, "%s object too high for %s space; %s inserted",
721 		    WARN, &fpos(y), num1, num2, KW_SCALE);
722 		else
723 		  Error(19, 7, "%s object too wide for %s space; %s inserted",
724 		    WARN, &fpos(y), num1, num2, KW_SCALE);
725 		y = InterposeScale(y, scale_factor, 1-dim);
726 		scaled = TRUE;
727 	      }
728 	    }
729 
730 	    /* otherwise we must reject, and warn the user */
731 	    if( !scaled )
732 	    {
733 	      debug3(DGA, D, "  reject: vsize %s,%s in %s; y=",
734 		EchoLength(perp_back), EchoLength(perp_fwd),
735 		EchoConstraint(&c));
736 	      ifdebug(DGA, DD, DebugObject(y));
737 	      goto REJECT;
738 	    }
739 
740 	  }
741 
742 	  /* dest seems OK, so perform its size adjustments */
743 	  debug0(DSA, D, "calling AdjustSize from AttachGalley (a)");
744 	  AdjustSize(dest, back(y, dim), fwd(y, dim), dim);
745 	  debug0(DSA, D, "calling AdjustSize from AttachGalley (b)");
746 	  AdjustSize(dest, perp_back, perp_fwd, 1-dim);
747 
748 
749 	  /* now check parallel space for target_galley in target */
750 	  Constrained(target, &c, dim, &why);
751 	  debug3(DGF, DD, "  target parallel Constrained(%s, %s) = %s",
752 	    EchoObject(target), dimen(dim), EchoConstraint(&c));
753 	  Child(z, LastDown(target_galley));  /* works in all cases? */
754 	  assert( !is_index(type(z)), "AttachGalley: is_index(z)!" );
755 	  assert( back(z, dim)>=0 && fwd(z, dim)>=0, "AttachGalley: z size!" );
756 	  if( !FitsConstraint(back(z, dim), fwd(z, dim), c) )
757 	  { BOOLEAN scaled;
758 
759 	    debug2(DGA, DD, "  why     = %d %s", (int) why, EchoObject(why));
760 	    debug2(DGA, DD, "  limiter = %d %s", (int) limiter(hd),
761 	      EchoObject(limiter(hd)));
762 
763 	    /* if forcing galley doesn't fit, try scaling z */
764 	    scaled = FALSE;
765 	    if( force_gall(hd) && size(z, dim) > 0 && limiter(hd) != why )
766 	    { int scale_factor;
767 	      scale_factor = ScaleToConstraint(back(z,dim), fwd(z,dim), &c);
768 	      if( scale_factor > 0.5 * SF )
769 	      {	char num1[20], num2[20];
770 		sprintf(num1, "%.1fc", (float) size(z, dim) / CM);
771 		sprintf(num2, "%.1fc", (float) bfc(c) / CM);
772 		if( dim == ROWM )
773 		  Error(19, 8, "%s object too high for %s space; %s inserted",
774 		    WARN, &fpos(y), num1, num2, KW_SCALE);
775 		else
776 		  Error(19, 9, "%s object too wide for %s space; %s inserted",
777 		    WARN, &fpos(y), num1, num2, KW_SCALE);
778 		z = InterposeWideOrHigh(z, dim);
779 		z = InterposeScale(z, scale_factor, dim);
780 		scaled = TRUE;
781 	      }
782 	    }
783 
784 	    if( !scaled )
785 	    { char num1[20], num2[20];
786 	      limiter(hd) = why;
787 	      debug3(DGA, D, "  set limiter(%s) = %d %s", SymName(actual(hd)),
788 		(int) limiter(hd), EchoObject(limiter(hd)));
789 	      debug3(DGA, D, "  reject: size was %s,%s in %s; y =",
790 		EchoLength(back(z, dim)), EchoLength(fwd(z, dim)),
791 		EchoConstraint(&c));
792 	      ifdebug(DGA, DD, DebugObject(y));
793 	      if( size(z, dim) > 0 )
794 	      { sprintf(num1, "%.1fc", (float) size(z, dim) / CM);
795 	        sprintf(num2, "%.1fc", (float) bfc(c) / CM);
796 	        if( dim == ROWM )
797 		  Error(19, 14, "%s object too high for %s space; will try elsewhere",
798 		    WARN, &fpos(y), num1, num2);
799 	        else
800 		  Error(19, 15, "%s object too wide for %s space; will try elsewhere",
801 		    WARN, &fpos(y), num1, num2);
802 	      }
803 	      goto REJECT;
804 	    }
805 	  }
806 	  limiter(hd) = why;
807 	  debug3(DGA, DD, "  set limiter(%s) = %d %s", SymName(actual(hd)),
808 	    (int) limiter(hd), EchoObject(limiter(hd)));
809 
810 	  /* now check perpendicular space for target_galley in target */
811 	  Constrained(target, &c, 1-dim, &junk);
812 	  debug3(DGF, DD, "  target perpendicular Constrained(%s, %s) = %s",
813 	    EchoObject(target), dimen(1-dim), EchoConstraint(&c));
814 	  Child(z, LastDown(target_galley));  /* works in all cases? */
815 	  assert( !is_index(type(z)), "AttachGalley: is_index(z)!" );
816 	  assert( back(z, 1-dim)>=0 && fwd(z, 1-dim)>=0,
817 	    "AttachGalley: z size (perpendicular)!" );
818 	  if( !FitsConstraint(back(z, 1-dim), fwd(z, 1-dim), c) )
819 	  { BOOLEAN scaled;
820 
821 	    /* if forcing galley doesn't fit, try scaling z */
822 	    scaled = FALSE;
823 	    if( force_gall(hd) && size(z, 1-dim) > 0 )
824 	    { int scale_factor;
825 	      scale_factor = ScaleToConstraint(back(z,1-dim), fwd(z,1-dim), &c);
826 	      if( scale_factor > 0.5 * SF )
827 	      {	char num1[20], num2[20];
828 		sprintf(num1, "%.1fc", (float) size(z, 1-dim) / CM);
829 		sprintf(num2, "%.1fc", (float) bfc(c) / CM);
830 		if( 1-dim == ROWM )
831 		  Error(19, 10, "%s object too high for %s space; %s inserted",
832 		    WARN, &fpos(y), num1, num2, KW_SCALE);
833 		else
834 		  Error(19, 11, "%s object too wide for %s space; %s inserted",
835 		    WARN, &fpos(y), num1, num2, KW_SCALE);
836 		z = InterposeWideOrHigh(z, 1-dim);
837 		z = InterposeScale(z, scale_factor, 1-dim);
838 		scaled = TRUE;
839 	      }
840 	    }
841 
842 	    if( !scaled )
843 	    {
844 	      debug3(DGA, D, "  reject: size was %s,%s in %s; y =",
845 		EchoLength(back(z, 1-dim)), EchoLength(fwd(z, 1-dim)),
846 		EchoConstraint(&c));
847 	      ifdebug(DGA, DD, DebugObject(y));
848 	      goto REJECT;
849 	    }
850 	  }
851 
852 	  /* target seems OK, so adjust sizes and accept */
853 	  if( external_hor(target) )
854 	  {
855 	    /* don't adjust any sizes, none to adjust */
856 	    debug0(DSA, D, "not calling AdjustSize from AttachGalley (c)");
857 	  }
858 	  else if( external_ver(target) )
859 	  {
860 	    /* adjust perp size only, to galley size */
861 	    debug0(DSA, D, "calling AdjustSize from AttachGalley (d)");
862 	    AdjustSize(target, back(target_galley, 1-dim),
863 	      fwd(target_galley, 1-dim), 1-dim);
864 	  }
865 	  else
866 	  {
867 	    /* adjust both directions, using z (last component) */
868 	    Child(z, LastDown(target_galley));
869 	    debug0(DSA, D, "AttachGalley AdjustSize using z =");
870 	    ifdebug(DSA, D, DebugObject(z));
871 	    debug0(DSA, D, "calling AdjustSize from AttachGalley (e)");
872 	    AdjustSize(target, back(z, dim), fwd(z, dim), dim);
873 	    debug0(DSA, D, "calling AdjustSize from AttachGalley (f)");
874 	    AdjustSize(target, back(z, 1-dim), fwd(z, 1-dim), 1-dim);
875 	  }
876 
877 	  goto ACCEPT;
878 
879 
880 	default:
881 
882 	  assert1(FALSE, "AttachGalley:", Image(type(y)));
883 	  break;
884 
885       } /* end switch */
886     } /* end for */
887 
888     /* null galley: promote whole galley without expanding the target */
889     debug0(DGA, D, "  null galley");
890     if( tg_inners != nilobj )  DisposeObject(tg_inners), tg_inners = nilobj;
891     DisposeObject(target_galley);
892     LeaveErrorBlock(FALSE);
893     debug0(DYY, D, "] LeaveErrorBlock(FALSE) (null galley)");
894 
895     /* kill off any null objects within the galley, then transfer it */
896     /* don't use Promote() since it does extra unwanted things here  */
897     for( link = Down(hd);  link != hd;  link = NextDown(link) )
898     { Child(y, link);
899       switch( type(y) )
900       {
901 
902 	case GAP_OBJ:
903 	case CLOSURE:
904 	case CROSS:
905 	case FORCE_CROSS:
906 	case NULL_CLOS:
907 	case PAGE_LABEL:
908 
909 	  link = PrevDown(link);
910 	  debug1(DGA, D, "  null galley, disposing %s", Image(type(y)));
911 	  DisposeChild(NextDown(link));
912 	  break;
913 
914 
915 	default:
916 
917 	  break;
918       }
919     }
920     TransferLinks(NextDown(hd), hd, Up(target_index));
921 
922     /* attach hd temporarily to target_index */
923     MoveLink(Up(hd), target_index, PARENT);
924     assert( type(hd_index) == UNATTACHED, "AttachGalley: type(hd_index)!" );
925     DeleteNode(hd_index);
926 
927     /* return; only hd_inners needs to be flushed now */
928     *inners = hd_inners;
929     debug0(DGA, D, "] AttachGalley returning ATTACH_NULL");
930     return ATTACH_NULL;
931 
932 
933     REJECT:
934 
935       /* reject first component */
936       /* debug1(DGA, D, "  reject %s", EchoObject(y)); */
937       debug0(DGA, D, "  reject first component");
938       LeaveErrorBlock(TRUE);
939       debug0(DYY, D, "] LeaveErrorBlock(TRUE) (REJECT)");
940       if( tg_inners != nilobj )  DisposeObject(tg_inners), tg_inners = nilobj;
941       DisposeObject(target_galley);
942       if( foll_or_prec(hd) == GALL_PREC && !sized(hd) )
943       {
944 	/* move to just before the failed target */
945 	MoveLink(Up(hd_index), Up(target_index), PARENT);
946       }
947       else
948       {
949 	/* move to just after the failed target */
950 	MoveLink(Up(hd_index), NextDown(Up(target_index)), PARENT);
951       }
952       continue;
953 
954 
955     SUSPEND:
956 
957       /* suspend at first component */
958       debug1(DGA, D, "  suspend %s", EchoIndex(y));
959       blocked(y) = TRUE;
960       LeaveErrorBlock(FALSE);
961       debug0(DYY, D, "] LeaveErrorBlock(FALSE) (SUSPEND)");
962       if( tg_inners != nilobj )  DisposeObject(tg_inners), tg_inners = nilobj;
963       DisposeObject(target_galley);
964       MoveLink(Up(hd_index), Up(target_index), PARENT);
965       if( was_sized )
966       { /* nothing new to flush if suspending and already sized */
967 	if( hd_inners != nilobj )  DisposeObject(hd_inners), hd_inners=nilobj;
968 	*inners = nilobj;
969       }
970       else
971       { /* flush newly discovered inners if not sized before */
972 	*inners = hd_inners;
973       }
974       debug0(DGA, D, "] AttachGalley returning ATTACH_SUSPEND");
975       *suspend_pt = y;
976       return ATTACH_SUSPEND;
977 
978 
979     ACCEPT:
980 
981       /* accept first component; now committed to the attach */
982       debug2(DGA, D, "  accept first component %s %s", Image(type(y)),
983 	EchoFilePos(&fpos(y)));
984       /* ***
985       debug3(DGA, DD, "  accept %s %s %s", Image(type(y)), EchoObject(y),
986 	EchoFilePos(&fpos(y)));
987       *** */
988       LeaveErrorBlock(TRUE);
989       debug0(DYY, D, "] LeaveErrorBlock(TRUE) (ACCEPT)");
990 
991       /* attach hd to dest */
992       MoveLink(Up(hd), dest_index, PARENT);
993       assert( type(hd_index) == UNATTACHED, "AttachGalley: type(hd_index)!" );
994       DeleteNode(hd_index);
995 
996       /* move first component of hd into dest */
997       /* nb Interpose must be done after all AdjustSize calls */
998       if( dim == ROWM && !external_ver(dest) )
999 	Interpose(dest, VCAT, hd, y);
1000       else if( dim == COLM && !external_hor(dest) )
1001       { Interpose(dest, ACAT, y, y);
1002 	Parent(junk, Up(dest));
1003 	assert( type(junk) == ACAT, "AttachGalley: type(junk) != ACAT!" );
1004 	StyleCopy(save_style(junk), save_style(dest));
1005 	adjust_cat(junk) = padjust(save_style(junk));
1006       }
1007       debug1(DGS, D, "calling Promote(hd, %s) from AttachGalley/ACCEPT",
1008 	link == hd ? "hd" : "NextDown(link)");
1009       Promote(hd, link == hd ? hd : NextDown(link), dest_index, TRUE);
1010 
1011       /* move target_galley into target */
1012       /* nb Interpose must be done after all AdjustSize calls */
1013       if( !(external_ver(target) || external_hor(target)) )
1014       {	Child(z, LastDown(target_galley));
1015 	Interpose(target, VCAT, z, z);
1016       }
1017       debug0(DGS, D, "calling Promote(target_galley) from AttachGalley/ACCEPT");
1018       Promote(target_galley, target_galley, target_index, TRUE);
1019       DeleteNode(target_galley);
1020       assert(Down(target_index)==target_index, "AttachGalley: target_ind");
1021       if( blocked(target_index) )  blocked(dest_index) = TRUE;
1022       DeleteNode(target_index);
1023 
1024       /* return; both tg_inners and hd_inners need to be flushed now;        */
1025       /* if was_sized, hd_inners contains the inners of the first component; */
1026       /* otherwise it contains the inners of all components, from SizeGalley */
1027       if( tg_inners == nilobj ) *inners = hd_inners;
1028       else if( hd_inners == nilobj ) *inners = tg_inners;
1029       else
1030       {	TransferLinks(Down(hd_inners), hd_inners, tg_inners);
1031 	DeleteNode(hd_inners);
1032 	*inners = tg_inners;
1033       }
1034       debug1(DGA, D, "] AttachGalley returning ATTACH_ACCEPT (inners %s)",
1035 	DebugInnersNames(*inners));
1036       ifdebug(DGA, D,
1037 	if( dim == COLM && !external_hor(dest) )
1038 	{ OBJECT z;
1039 	  Parent(z, Up(dest));
1040 	  debug2(DGA, D, "  COLM dest_encl on exit = %s %s",
1041 	    Image(type(z)), EchoObject(z));
1042 	}
1043       )
1044       return ATTACH_ACCEPT;
1045 
1046   } /* end for */
1047 } /* end AttachGalley */
1048