1 /*@z07.c:Object Service:SplitIsDefinite(), DisposeObject()@*******************/
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:         z07.c                                                      */
26 /*  MODULE:       Object Service                                             */
27 /*  EXTERNS:      MakeWord(), MakeWordTwo(), MakeWordThree(),                */
28 /*                DisposeObject(), CopyObject(),                             */
29 /*                SplitIsDefinite(), InsertObject()                          */
30 /*                                                                           */
31 /*****************************************************************************/
32 #include "externs.h"
33 
34 
35 /*****************************************************************************/
36 /*                                                                           */
37 /*  BOOLEAN SplitIsDefinite(x)                                               */
38 /*                                                                           */
39 /*  Return TRUE if x is a definite SPLIT object (both children definite)     */
40 /*                                                                           */
41 /*****************************************************************************/
42 
SplitIsDefinite(OBJECT x)43 BOOLEAN SplitIsDefinite(OBJECT x)
44 { OBJECT y1, y2;
45   assert( type(x) == SPLIT, "SplitIsDefinite: x not a SPLIT!" );
46   Child(y1, DownDim(x, COLM));
47   Child(y2, DownDim(x, ROWM));
48   return is_definite(type(y1)) && is_definite(type(y2));
49 } /* end SplitIsDefinite */
50 
51 
52 /*****************************************************************************/
53 /*                                                                           */
54 /*  DisposeSplitObject(x)                                                    */
55 /*                                                                           */
56 /*  Dispose SPLIT object x, taking care to handle COL_THR and ROW_THR        */
57 /*  children properly.                                                       */
58 /*                                                                           */
59 /*****************************************************************************/
60 
DisposeSplitObject(OBJECT x)61 static void DisposeSplitObject(OBJECT x)
62 { int i, count;
63   OBJECT y, link, uplink;
64   debug1(DOS, DDD, "[ DisposeSplitObject( %ld )", (long) x);
65   assert(type(x) == SPLIT, "DisposeSplitObject: type(x) != SPLIT!");
66   assert(Down(x) != x, "DisposeSplitObject: x has no children!")
67   assert(LastDown(x) != Down(x), "DisposeSplitObject: x has one child!")
68   assert(LastDown(x) == NextDown(Down(x)), "DisposeSplitObject: children!")
69 
70   /* handle first child */
71   CountChild(y, Down(x), count);
72   if( type(y) == COL_THR )
73   {
74     /* find corresponding child link out of y and delete that link */
75     for( link = Down(y), uplink = Up(y), i = 1;
76          link != y && uplink != y && i < count;
77          link = NextDown(link), uplink = NextUp(uplink), i++ );
78     assert( link != y && uplink != y, "DisposeSplitObject: link (a)!" );
79     DisposeChild(link);
80   }
81   DisposeChild(Down(x));
82 
83   /* handle second child */
84   CountChild(y, LastDown(x), count);
85   if( type(y) == ROW_THR )
86   {
87     /* find corresponding child link out of y and delete that link */
88     for( link = Down(y), uplink = Up(y), i = 1;
89          link != y && uplink != y && i < count;
90          link = NextDown(link), uplink = NextUp(uplink), i++ );
91     assert( link != y && uplink != y, "DisposeSplitObject: link (b)!" );
92     DisposeChild(link);
93   }
94   DisposeChild(LastDown(x));
95   debug0(DOS, DDD, "] DisposeSplitObject returning");
96 } /* end DisposeSplitObject */
97 
98 
99 /*****************************************************************************/
100 /*                                                                           */
101 /*  DisposeObject(x)                                                         */
102 /*                                                                           */
103 /*  Dispose object x recursively, leaving intact any shared descendants.     */
104 /*  We return a useless integer so that we can use this in expresssions.     */
105 /*                                                                           */
106 /*  If x is a SPLIT object then one or both of its children could be         */
107 /*  COL_THR or ROW_THR objects.  If such thread object is has this SPLIT     */
108 /*  as its ith parent, then we need to dispose its ith child.                */
109 /*                                                                           */
110 /*****************************************************************************/
111 
DisposeObject(OBJECT x)112 int DisposeObject(OBJECT x)
113 { debug2(DOS,DDD,"[DisposeObject( %ld ), type = %s, x =", (long) x, Image(type(x)));
114   ifdebug(DOS, DDD, DebugObject(x));
115   assert( Up(x) == x, "DisposeObject: x has a parent!" );
116   if( type(x) == SPLIT )
117     DisposeSplitObject(x);
118   else
119   { while( Down(x) != x )  DisposeChild(Down(x));
120     Dispose(x);
121   }
122   debug0(DOS, DDD, "]DisposeObject returning.");
123   return 0;
124 } /* end DisposeObject */
125 
126 
127 /*@::MakeWord(), MakeWordTwo()@***********************************************/
128 /*                                                                           */
129 /*  OBJECT MakeWord(typ, str, pos)                                           */
130 /*                                                                           */
131 /*  Return an unsized WORD or QWORD made from the given string and fpos.     */
132 /*                                                                           */
133 /*****************************************************************************/
134 
MakeWord(unsigned typ,FULL_CHAR * str,FILE_POS * pos)135 OBJECT MakeWord(unsigned typ, FULL_CHAR *str, FILE_POS *pos)
136 { OBJECT res;
137   NewWord(res, typ, StringLength(str), pos);
138   StringCopy(string(res), str);
139   FposCopy(fpos(res), *pos);
140   debug4(DOS, DDD, "MakeWord(%s, %s, %s) returning %s",
141     Image(typ), str, EchoFilePos(pos), EchoObject(res));
142   return res;
143 } /* end MakeWord */
144 
145 
146 /*****************************************************************************/
147 /*                                                                           */
148 /*  OBJECT MakeWordTwo(typ, str1, str2, pos)                                 */
149 /*                                                                           */
150 /*  Return an unsized WORD or QWORD made from the two strings and fpos.      */
151 /*                                                                           */
152 /*****************************************************************************/
153 
MakeWordTwo(unsigned typ,FULL_CHAR * str1,FULL_CHAR * str2,FILE_POS * pos)154 OBJECT MakeWordTwo(unsigned typ, FULL_CHAR *str1, FULL_CHAR *str2, FILE_POS *pos)
155 { int len1 = StringLength(str1);
156   int len2 = StringLength(str2);
157   OBJECT res;
158   debug4(DOS, DDD, "MakeWordTwo(%s, %s, %s, %s)",
159     Image(typ), str1, str2, EchoFilePos(pos));
160   NewWord(res, typ, len1 + len2, pos);
161   StringCopy(string(res), str1);
162   StringCopy(&string(res)[len1], str2);
163   FposCopy(fpos(res), *pos);
164   debug5(DOS, DDD, "MakeWordTwo(%s, %s, %s, %s) returning %s",
165     Image(typ), str1, str2, EchoFilePos(pos), EchoObject(res));
166   return res;
167 } /* end MakeWordTwo */
168 
169 
170 /*****************************************************************************/
171 /*                                                                           */
172 /*  OBJECT MakeWordThree(s1, s2, s3)                                         */
173 /*                                                                           */
174 /*  Return an unsized WORD containing these three strings.                   */
175 /*                                                                           */
176 /*****************************************************************************/
177 
MakeWordThree(FULL_CHAR * s1,FULL_CHAR * s2,FULL_CHAR * s3)178 OBJECT MakeWordThree(FULL_CHAR *s1, FULL_CHAR *s2, FULL_CHAR *s3)
179 { int len1 = StringLength(s1);
180   int len2 = StringLength(s2);
181   int len3 = StringLength(s3);
182   OBJECT res;
183   debug3(DOS, DDD, "MakeWordThree(%s, %s, %s)", s1, s2, s3);
184   NewWord(res, WORD, len1 + len2 + len3, no_fpos);
185   StringCopy(string(res), s1);
186   StringCopy(&string(res)[len1], s2);
187   StringCopy(&string(res)[len1 + len2], s3);
188   debug4(DOS, DDD, "MakeWordThree(%s, %s, %s) returning %s",
189     s1, s2, s3, EchoObject(res));
190   return res;
191 } /* end MakeWordThree */
192 
193 
194 /*@::CopyObject()@************************************************************/
195 /*                                                                           */
196 /*  OBJECT CopyObject(x, pos)                                                */
197 /*                                                                           */
198 /*  Make a copy of unsized object x, setting all file positions to *pos,     */
199 /*  unless *pos is no_fpos, in which case set all file positions to what     */
200 /*  they are in the object being copied.                                     */
201 /*                                                                           */
202 /*****************************************************************************/
203 
CopyObject(OBJECT x,FILE_POS * pos)204 OBJECT CopyObject(OBJECT x, FILE_POS *pos)
205 { OBJECT y, link, res, tmp;
206 
207   debug2(DOS, DDD, "[ CopyObject(%s, %s)", EchoObject(x), EchoFilePos(pos));
208   switch( type(x) )
209   {
210 
211     case WORD:
212     case QWORD:
213 
214       NewWord(res, type(x), StringLength(string(x)), pos);
215       StringCopy(string(res), string(x));
216       break;
217 
218 
219     case GAP_OBJ:
220 
221       New(res, GAP_OBJ);
222       GapCopy(gap(res), gap(x));
223       hspace(res) = hspace(x);
224       vspace(res) = vspace(x);
225       if( Down(x) != x )
226       {	Child(y, Down(x));
227 	tmp = CopyObject(y, pos);
228 	Link(res, tmp);
229       }
230       break;
231 
232 
233     /* case HEAD: */
234     case NULL_CLOS:
235     case PAGE_LABEL:
236     case CROSS:
237     case FORCE_CROSS:
238     case BEGIN_HEADER:
239     case END_HEADER:
240     case SET_HEADER:
241     case CLEAR_HEADER:
242     case ONE_COL:
243     case ONE_ROW:
244     case WIDE:
245     case HIGH:
246     case HSHIFT:
247     case VSHIFT:
248     case HMIRROR:
249     case VMIRROR:
250     case HSCALE:
251     case VSCALE:
252     case HCOVER:
253     case VCOVER:
254     case SCALE:
255     case KERN_SHRINK:
256     case HCONTRACT:
257     case VCONTRACT:
258     case HLIMITED:
259     case VLIMITED:
260     case HEXPAND:
261     case VEXPAND:
262     case START_HVSPAN:
263     case START_HSPAN:
264     case START_VSPAN:
265     case HSPAN:
266     case VSPAN:
267     case PADJUST:
268     case HADJUST:
269     case VADJUST:
270     case ROTATE:
271     case BACKGROUND:
272     case RAW_VERBATIM:
273     case VERBATIM:
274     case CASE:
275     case YIELD:
276     case BACKEND:
277     case XCHAR:
278     case FONT:
279     case SPACE:
280     case YUNIT:
281     case ZUNIT:
282     case SET_CONTEXT:
283     case GET_CONTEXT:
284     case BREAK:
285     case UNDERLINE:
286     case UNDERLINE_COLOUR:
287     case COLOUR:
288     case TEXTURE:
289     case OUTLINE:
290     case LANGUAGE:
291     case CURR_LANG:
292     case CURR_FAMILY:
293     case CURR_FACE:
294     case CURR_YUNIT:
295     case CURR_ZUNIT:
296     case COMMON:
297     case RUMP:
298     case MELD:
299     case INSERT:
300     case ONE_OF:
301     case NEXT:
302     case PLUS:
303     case MINUS:
304     case OPEN:
305     case TAGGED:
306     case INCGRAPHIC:
307     case SINCGRAPHIC:
308     case PLAIN_GRAPHIC:
309     case GRAPHIC:
310     case LINK_SOURCE:
311     case LINK_DEST:
312     case LINK_DEST_NULL:
313     case LINK_URL:
314     case VCAT:
315     case HCAT:
316     case ACAT:
317     case ENV_OBJ:
318 
319       New(res, type(x));
320       for( link = Down(x);  link != x;  link = NextDown(link) )
321       {	Child(y, link);
322 	tmp = CopyObject(y, pos);
323 	Link(res, tmp);
324       }
325       break;
326 
327 
328     case FILTERED:
329 
330       New(res, type(x));
331       for( link = Down(x);  link != x;  link = NextDown(link) )
332       {	Child(y, link);
333 	Link(res, y);	/* do not copy children of FILTERED */
334       }
335       debug3(DFH, D, "copying FILTERED %d into %d %s",
336 	(int) x, (int) res, EchoObject(res));
337       break;
338 
339 
340     case ENV:
341 
342       res = x;  /* do not copy environments */
343       break;
344 
345 
346     case PAR:
347 
348       New(res, PAR);
349       actual(res) = actual(x);
350       assert( Down(x) != x, "CopyObject: PAR child!" );
351       Child(y, Down(x));
352       tmp = CopyObject(y, pos);
353       Link(res, tmp);
354       break;
355 
356 
357     case CLOSURE:
358 
359       New(res, CLOSURE);
360       for( link = Down(x);  link != x;  link = NextDown(link) )
361       {	Child(y, link);
362 	assert( type(y) != CLOSURE, "CopyObject: CLOSURE!" );
363 	tmp = CopyObject(y, pos);
364 	Link(res, tmp);
365       }
366       actual(res) = actual(x);
367       StyleCopy(save_style(res), save_style(x));
368       break;
369 
370 
371     default:
372 
373       assert1(FALSE, "CopyObject:", Image(type(x)));
374       res = nilobj;
375       break;
376 
377   } /* end switch */
378   if( pos == no_fpos )  FposCopy(fpos(res), fpos(x));
379   else FposCopy(fpos(res), *pos);
380   debug1(DOS, DDD, "] CopyObject returning %s", EchoObject(res));
381   return res;
382 } /* end CopyObject */
383 
384 
385 /*****************************************************************************/
386 /*                                                                           */
387 /*  OBJECT InsertObject(OBJECT x, OBJECT *ins, STYLE *style)                 */
388 /*                                                                           */
389 /*  Search through manifested object x for an ACAT where ins may be          */
390 /*  attached.  If successful, set *ins to nilobj after the attachment.       */
391 /*                                                                           */
392 /*****************************************************************************/
393 
InsertObject(OBJECT x,OBJECT * ins,STYLE * style)394 OBJECT InsertObject(OBJECT x, OBJECT *ins, STYLE *style)
395 { OBJECT link, y, g, res;
396   debug2(DOS, D, "InsertObject(%s, %s)", EchoObject(x), EchoObject(*ins));
397   switch( type(x) )
398   {
399     case WORD:
400     case QWORD:
401 
402       New(res, ACAT);
403       FposCopy(fpos(res), fpos(x));
404       ReplaceNode(res, x);
405       Link(res, x);
406       StyleCopy(save_style(res), *style);
407       adjust_cat(res) = padjust(*style);
408       res = InsertObject(res, ins, style);
409       break;
410 
411 
412     case NULL_CLOS:
413     case BEGIN_HEADER:
414     case END_HEADER:
415     case SET_HEADER:
416     case CLEAR_HEADER:
417     case HEAD:
418     case CROSS:
419     case FORCE_CROSS:
420     case PAGE_LABEL:
421     case CLOSURE:
422     case INCGRAPHIC:
423     case SINCGRAPHIC:
424     case HSPAN:
425     case VSPAN:
426 
427       res = x;
428       break;
429 
430 
431     case HCAT:
432     case VCAT:
433     case COL_THR:
434     case ROW_THR:
435     case SPLIT:
436 
437       for( link = Down(x);  link != x && *ins != nilobj;  link = NextDown(link) )
438       { Child(y, link);
439 	y = InsertObject(y, ins, style);
440       }
441       res = x;
442       break;
443 
444 
445     case ONE_COL:
446     case ONE_ROW:
447     case PADJUST:
448     case HADJUST:
449     case VADJUST:
450     case HCONTRACT:
451     case VCONTRACT:
452     case HLIMITED:
453     case VLIMITED:
454     case HEXPAND:
455     case VEXPAND:
456     case HMIRROR:
457     case VMIRROR:
458     case HSCALE:
459     case VSCALE:
460     case HCOVER:
461     case VCOVER:
462     case PLAIN_GRAPHIC:
463     case GRAPHIC:
464     case LINK_SOURCE:
465     case LINK_DEST:
466     case LINK_DEST_NULL:
467     case LINK_URL:
468     case ROTATE:
469     case BACKGROUND:
470     case SCALE:
471     case KERN_SHRINK:
472     case WIDE:
473     case HIGH:
474     case HSHIFT:
475     case VSHIFT:
476     case START_HVSPAN:
477     case START_HSPAN:
478     case START_VSPAN:
479 
480       Child(y, LastDown(x));
481       y = InsertObject(y, ins, style);
482       res = x;
483       break;
484 
485 
486     case ACAT:
487 
488       New(g, GAP_OBJ);
489       SetGap(gap(g), FALSE, FALSE, TRUE, FIXED_UNIT, EDGE_MODE, 0);
490       hspace(g) = vspace(g) = 0;
491       underline(g) = UNDER_OFF;
492       Link(Down(x), g);
493       Link(Down(x), *ins);
494       underline(*ins) = UNDER_OFF;
495       *ins = nilobj;
496       res = x;
497       break;
498 
499 
500     default:
501 
502       assert1(FALSE, "InsertObject:", Image(type(x)));
503       res = x;
504       break;
505 
506   }
507   debug2(DOS, D, "InsertObject returning (%s) %s",
508     *ins == nilobj ? "success" : "failure", EchoObject(res));
509   return res;
510 } /* end InsertObject */
511 
512 
513 /*****************************************************************************/
514 /*                                                                           */
515 /*  Meld(x, y)                                                               */
516 /*                                                                           */
517 /*  Return the meld of x with y.                                             */
518 /*                                                                           */
519 /*****************************************************************************/
520 #define NO_DIR	0
521 #define X_DIR	1
522 #define Y_DIR	2
523 #define XY_DIR	3
524 #define MAX_MELD 32
525 
Meld(OBJECT x,OBJECT y)526 OBJECT Meld(OBJECT x, OBJECT y)
527 { OBJECT res;
528   char table[MAX_MELD][MAX_MELD], dir[MAX_MELD][MAX_MELD];
529   OBJECT xcomp[MAX_MELD], ycomp[MAX_MELD];
530   OBJECT xgaps[MAX_MELD], ygaps[MAX_MELD];
531   BOOLEAN is_equal;
532   OBJECT link, z = nilobj, g;  BOOLEAN jn;
533   int xlen, ylen, xi, yi;
534   debug2(DOS, D, "Meld(%s, %s)", EchoObject(x), EchoObject(y));
535   assert(type(x) == ACAT, "Meld: type(x) != ACAT");
536   assert(type(y) == ACAT, "Meld: type(y) != ACAT");
537 
538   /* initialize xcomp, xgaps, xlen */
539   debug0(DOS, DD, "  initializing xcomp[]");
540   xlen = 0;
541   xcomp[xlen] = nilobj;
542   xlen++;
543   g = nilobj;
544   FirstDefinite(x, link, z, jn);
545   while( link != x )
546   { if( xlen >= MAX_MELD )
547       Error(7, 1, "%s: maximum paragraph length (%d) exceeded", FATAL, &fpos(x),
548 	KW_MELD, MAX_MELD-1);
549     assert( type(z) != ACAT, "Meld: xcomp is ACAT!");
550     if( g == nilobj || width(gap(g)) != 0 )
551     {
552       debug3(DOS, DD, "  initializing xcomp[%d] to %s %s",
553         xlen, Image(type(z)), EchoObject(z));
554       xcomp[xlen] = z;
555       xgaps[xlen] = g;
556       xlen++;
557     }
558     else
559     {
560       debug3(DOS, DD, "  extending xcomp[%d] with %s %s",
561         xlen-1, Image(type(z)), EchoObject(z));
562       if( type(xcomp[xlen-1]) != ACAT )
563       {
564 	New(res, ACAT);
565 	StyleCopy(save_style(res), save_style(x));
566 	Link(res, xcomp[xlen-1]);
567 	xcomp[xlen-1] = res;
568       }
569       Link(xcomp[xlen-1], g);
570       Link(xcomp[xlen-1], z);
571     }
572     NextDefiniteWithGap(x, link, z, g, jn)
573   }
574 
575   /* initialize ycomp, ygaps, ylen */
576   debug0(DOS, DD, "  initializing ycomp[]");
577   ylen = 0;
578   ycomp[ylen] = nilobj;
579   ylen++;
580   g = nilobj;
581   FirstDefinite(y, link, z, jn);
582   while( link != y )
583   { if( ylen >= MAX_MELD )
584       Error(7, 1, "%s: maximum paragraph length (%d) exceeded", FATAL, &fpos(y),
585 	KW_MELD, MAX_MELD-1);
586     assert( type(z) != ACAT, "Meld: ycomp is ACAT!");
587     if( g == nilobj || width(gap(g)) != 0 )
588     {
589       debug3(DOS, DD, "  initializing ycomp[%d] to %s %s",
590         ylen, Image(type(z)), EchoObject(z));
591       ycomp[ylen] = z;
592       ygaps[ylen] = g;
593       ylen++;
594     }
595     else
596     {
597       debug3(DOS, DD, "  extending ycomp[%d] with %s %s",
598         ylen-1, Image(type(z)), EchoObject(z));
599       if( type(ycomp[ylen-1]) != ACAT )
600       {
601 	New(res, ACAT);
602 	StyleCopy(save_style(res), save_style(x));
603 	Link(res, ycomp[ylen-1]);
604 	ycomp[ylen-1] = res;
605       }
606       Link(ycomp[ylen-1], g);
607       Link(ycomp[ylen-1], z);
608     }
609     NextDefiniteWithGap(y, link, z, g, jn)
610   }
611 
612   /* initialize table and dir */
613   debug0(DOS, DD, "  initializing table[]");
614   table[0][0] = 0;
615   dir[0][0] = NO_DIR;
616   for( xi = 1;  xi < xlen;  xi++ )
617   { table[xi][0] = 0;
618     dir[xi][0] = X_DIR;
619   }
620   for( yi = 1;  yi < ylen;  yi++ )
621   { table[0][yi] = 0;
622     dir[0][yi] = Y_DIR;
623   }
624   for( xi = 1;  xi < xlen;  xi++ )
625   {
626     for( yi = 1;  yi < ylen;  yi++ )
627     {
628       is_equal = EqualManifested(xcomp[xi], ycomp[yi]);
629       if( is_equal )
630       {
631 	table[xi][yi] = 1 + table[xi - 1][yi - 1];
632 	dir[xi][yi] = XY_DIR;
633         debug3(DOS, DD, "  assigning (XY) table[%d][%d] = %d", xi, yi,
634 	  table[xi][yi]);
635       }
636       else if( table[xi - 1][yi] > table[xi][yi - 1] )
637       {
638 	table[xi][yi] = table[xi - 1][yi];
639 	dir[xi][yi] = X_DIR;
640         debug3(DOS, DD, "  assigning (X) table[%d][%d] = %d", xi, yi,
641 	  table[xi][yi]);
642       }
643       else
644       {
645 	table[xi][yi] = table[xi][yi - 1];
646 	dir[xi][yi] = Y_DIR;
647         debug3(DOS, DD, "  assigning (Y) table[%d][%d] = %d", xi, yi,
648 	  table[xi][yi]);
649       }
650     }
651   }
652 
653   /* traverse table from [xlen-l][ylen-1] back to [0][0], finding who's in */
654   debug0(DOS, DD, "  traversing table[]");
655   New(res, ACAT);
656   StyleCopy(save_style(res), save_style(x));
657   for( xi = xlen - 1, yi = ylen - 1;  dir[xi][yi] != NO_DIR; )
658   {
659     switch( dir[xi][yi] )
660     {
661       case XY_DIR:
662 
663         debug3(DOS, DD, "  at table[%d][%d] (XY) linking %s",
664 	  xi, yi, EchoObject(xcomp[xi]));
665 	if( type(xcomp[xi]) != ACAT )
666 	{
667           Link(Down(res), xcomp[xi]);
668 	}
669 	else
670 	  TransferLinks(Down(xcomp[xi]), xcomp[xi], Down(res));
671         g = xgaps[xi];
672         xi--;
673         yi--;
674 	break;
675 
676 
677       case Y_DIR:
678 
679         debug3(DOS, DD, "  at table[%d][%d] (ydec) linking %s",
680 	  xi, yi, EchoObject(ycomp[yi]));
681 	if( type(ycomp[yi]) != ACAT )
682 	{
683           Link(Down(res), ycomp[yi]);
684 	}
685 	else
686 	  TransferLinks(Down(ycomp[yi]), ycomp[yi], Down(res));
687         g = ygaps[yi];
688         yi--;
689 	break;
690 
691 
692       case X_DIR:
693 
694         debug3(DOS, DD, "  at table[%d][%d] (xdec) linking %s",
695 	  xi, yi, EchoObject(xcomp[xi]));
696 	if( type(xcomp[xi]) != ACAT )
697 	{
698           Link(Down(res), xcomp[xi]);
699 	}
700 	else
701 	  TransferLinks(Down(xcomp[xi]), xcomp[xi], Down(res));
702         g = xgaps[xi];
703         xi--;
704     }
705 
706     /* add gap if not last time; either g or one we make up */
707     if( dir[xi][yi] != NO_DIR )
708     {
709       if( g == nilobj )
710       {
711 	OBJECT tmp;
712 	New(g, GAP_OBJ);
713 	hspace(g) = 1;  vspace(g) = 0;
714 	FposCopy(fpos(g), *no_fpos);
715 	SetGap(gap(g), FALSE, FALSE, TRUE, FIXED_UNIT, EDGE_MODE,
716 	  width(space_gap(save_style(res))));
717 	tmp = MakeWord(WORD, AsciiToFull("1s"), &fpos(g));
718 	Link(g, tmp);
719         Link(Down(res), g);
720       }
721       else
722       {
723 	assert(Up(g) == LastUp(g), "Meld: g!" );
724         Link(Down(res), g);
725       }
726     }
727   }
728 
729   debug1(DOS, D, "Meld returning %s", EchoObject(res));
730   return res;
731 }
732 
733 
734 /*****************************************************************************/
735 /*                                                                           */
736 /*  static BOOLEAN EqualChildren(x, y)                                       */
737 /*                                                                           */
738 /*  Return TRUE if manifested objects x and y have equal children.           */
739 /*                                                                           */
740 /*****************************************************************************/
741 
EqualChildren(OBJECT x,OBJECT y)742 static BOOLEAN EqualChildren(OBJECT x, OBJECT y)
743 { OBJECT xl, yl, xc, yc;
744   xl = Down(x), yl = Down(y);
745   for( ; xl != x && yl != y;  xl = NextDown(xl), yl = NextDown(yl) )
746   {
747     Child(xc, xl);
748     Child(yc, yl);
749     if( !EqualManifested(xc, yc) )
750       return FALSE;
751   }
752   return xl == x && yl == y;
753 }
754 
755 
756 /*****************************************************************************/
757 /*                                                                           */
758 /*  BOOLEAN EqualManifested(x, y)                                            */
759 /*                                                                           */
760 /*  Return TRUE if manifested objects x and y are equal.                     */
761 /*                                                                           */
762 /*****************************************************************************/
763 
EqualManifested(OBJECT x,OBJECT y)764 BOOLEAN EqualManifested(OBJECT x, OBJECT y)
765 { OBJECT xc, yc;
766 
767   if( is_word(type(x)) && is_word(type(y)) )
768   {
769     return StringEqual(string(x), string(y));
770   }
771   else if( type(x) != type(y) )
772   {
773     return FALSE;
774   }
775   else switch( type(x) )
776   {
777     case GAP_OBJ:
778 
779       /* objects are equal if the two gaps are equal */
780       return GapEqual(gap(x), gap(y));
781       break;
782 
783 
784     case CLOSURE:
785 
786       /* objects are equal if it's the same symbol and same parameters */
787       if( actual(x) != actual(y) )
788 	return FALSE;
789       return EqualChildren(x, y);
790       break;
791 
792 
793     case PAGE_LABEL:
794     case NULL_CLOS:
795     case CROSS:
796     case FORCE_CROSS:
797     case HEAD:
798     case SPLIT:
799     case HSPANNER:
800     case VSPANNER:
801     case COL_THR:
802     case ROW_THR:
803     case ACAT:
804     case HCAT:
805     case VCAT:
806     case HMIRROR:
807     case VMIRROR:
808     case HSCALE:
809     case VSCALE:
810     case BEGIN_HEADER:
811     case SET_HEADER:
812     case END_HEADER:
813     case CLEAR_HEADER:
814     case ONE_COL:
815     case ONE_ROW:
816     case HCOVER:
817     case VCOVER:
818     case HCONTRACT:
819     case VCONTRACT:
820     case HEXPAND:
821     case VEXPAND:
822     case START_HSPAN:
823     case START_VSPAN:
824     case START_HVSPAN:
825     case HSPAN:
826     case VSPAN:
827     case KERN_SHRINK:
828     case BACKGROUND:
829     case GRAPHIC:
830     case PLAIN_GRAPHIC:
831     case LINK_DEST:
832     case LINK_DEST_NULL:
833     case LINK_URL:
834     case INCGRAPHIC:
835     case SINCGRAPHIC:
836     case PAR:
837 
838       /* objects are equal if the children are equal */
839       return EqualChildren(x, y);
840       break;
841 
842 
843     case LINK_SOURCE:
844 
845       /* objects are equal if right children are equal */
846       Child(xc, LastDown(x));
847       Child(yc, LastDown(y));
848       return EqualManifested(xc, yc);
849       break;
850 
851 
852     case WIDE:
853     case HIGH:
854 
855       /* objects are equal if constraints and children are equal */
856       return EqualConstraint(constraint(x), constraint(y)) &&
857 	     EqualChildren(x, y);
858       break;
859 
860 
861     case HSHIFT:
862     case VSHIFT:
863 
864       /* objects are equal if constraints and children are equal */
865       return shift_type(x) == shift_type(y) &&
866 	     GapEqual(shift_gap(x), shift_gap(y)) && EqualChildren(x, y);
867       break;
868 
869 
870     case SCALE:
871 
872       /* objects are equal if constraints and children are equal */
873       return bc(constraint(x)) == bc(constraint(y)) &&
874 	     fc(constraint(x)) == fc(constraint(y)) &&
875 	     EqualChildren(x, y);
876       break;
877 
878 
879     case ROTATE:
880 
881       /* objects are equal if angle is equal and children are equal */
882       return sparec(constraint(x)) == sparec(constraint(y)) &&
883 	     EqualChildren(x, y);
884       break;
885 
886 
887     default:
888 
889       Error(7, 2, "EqualUnsized: type == %s", FATAL, &fpos(x), Image(type(x)));
890       return FALSE;
891       break;
892   }
893 }
894