home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / APPS / lout2.lzh / LOUT2 / z10.c < prev    next >
Text File  |  1994-01-23  |  27KB  |  726 lines

  1. /*@z10.c:Cross References:CrossInit(), CrossMake()@***************************/
  2. /*                                                                           */
  3. /*  LOUT: A HIGH-LEVEL LANGUAGE FOR DOCUMENT FORMATTING (VERSION 2.05)       */
  4. /*  COPYRIGHT (C) 1993 Jeffrey H. Kingston                                   */
  5. /*                                                                           */
  6. /*  Jeffrey H. Kingston (jeff@cs.su.oz.au)                                   */
  7. /*  Basser Department of Computer Science                                    */
  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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.                */
  24. /*                                                                           */
  25. /*  FILE:         z10.c                                                      */
  26. /*  MODULE:       Cross References                                           */
  27. /*  EXTERNS:      CrossInit(), CrossMake(), GallTargEval(), CrossAddTag(),   */
  28. /*                CrossExpand(), CrossSequence(), CrossClose()               */
  29. /*                                                                           */
  30. /*****************************************************************************/
  31. #include "externs"
  32. #define    CROSS_LIT    CROSS_TARG
  33. #define    NO_TARGET    0
  34. #define    SEEN_TARGET    1
  35. #define    WRITTEN_TARGET    2
  36. static OBJECT RootCross = nil;            /* header for all crs        */
  37.  
  38.  
  39. /*****************************************************************************/
  40. /*                                                                           */
  41. /*  CrossInit(sym)     Initialize cross_sym(sym).                            */
  42. /*                                                                           */
  43. /*****************************************************************************/
  44.  
  45. CrossInit(sym)
  46. OBJECT sym;
  47. { int i; OBJECT cs = New(CROSS_SYM);
  48.   target_state(cs) = NO_TARGET;  target_seq(cs) = 0;
  49.   cr_file(cs) = NO_FILE;
  50.   gall_seq(cs) = 0;  gall_tag(cs) = nil;
  51.   gall_tfile(cs) = NO_FILE;  gentag_file(cs) = NO_FILE;
  52.   symb(cs) = sym;  cross_sym(sym) = cs;
  53.   gentag_fseq(cs) = NewWord(WORD, MAX_FILES, no_fpos);
  54.   for( i = 0;  i < MAX_FILES;  i++ ) string(gentag_fseq(cs))[i] = 0;
  55.   if( RootCross == nil )  RootCross = New(CR_ROOT);  Link(RootCross, cs);
  56. }
  57.  
  58.  
  59. /*****************************************************************************/
  60. /*                                                                           */
  61. /*  OBJECT CrossMake(sym, val, ctype)                                        */
  62. /*                                                                           */
  63. /*  Make a cross-reference with the given sym and tag value (NB no fpos).    */
  64. /*                                                                           */
  65. /*****************************************************************************/
  66.  
  67. OBJECT CrossMake(sym, val, ctype)
  68. OBJECT sym, val;  int ctype;
  69. { OBJECT v1, res;
  70.   debug3(DCR, DD, "CrossMake(%s, %s, %s)", SymName(sym),
  71.     EchoObject(val), Image(ctype));
  72.   res = New(CROSS);  cross_type(res) = ctype;  threaded(res) = FALSE;
  73.   v1 = New(CLOSURE);  actual(v1) = sym;
  74.   Link(res, v1);  Link(res, val);
  75.   debug1(DCR, DD, "CrossMake returning %s", EchoObject(res));
  76.   return res;
  77. }
  78.  
  79. /*@::GallTargEval(), CrossGenTag()@*******************************************/
  80. /*                                                                           */
  81. /*  OBJECT GallTargEval(sym, dfpos)                                          */
  82. /*                                                                           */
  83. /*  Produce a suitable cross-reference for a galley target.                  */
  84. /*                                                                           */
  85. /*****************************************************************************/
  86.  
  87. OBJECT GallTargEval(sym, dfpos)
  88. OBJECT sym; FILE_POS *dfpos;
  89. { OBJECT cs, res;
  90.   FULL_CHAR buff[MAX_LINE], *str;
  91.   debug2(DCR, DD, "GallTargEval( %s,%s )", SymName(sym), EchoFilePos(dfpos));
  92.   if( cross_sym(sym) == nil )  CrossInit(sym);
  93.   cs = cross_sym(sym);
  94.   if( file_num(*dfpos) != gall_tfile(cs) )
  95.   { gall_tfile(cs) = file_num(*dfpos);
  96.     gall_seq(cs)   = 0;
  97.   }
  98.   str = FileName(gall_tfile(cs));
  99.   if( StringLength(str) + 6 >= MAX_LINE )
  100.     Error(FATAL, dfpos, "automatically generated tag %s&%d is too long",
  101.     str, ++gall_seq(cs));
  102.   ++gall_seq(cs);
  103.   StringCopy(buff, str);
  104.   StringCat(buff, AsciiToFull("&"));
  105.   StringCat(buff, StringInt(gall_seq(cs)));
  106.   res = CrossMake(sym, MakeWord(WORD, buff, dfpos), GALL_TARG);
  107.   debug1(DCR, DD, "GallTargEval returning %s", EchoObject(res));
  108.   return res;
  109. } /* end GallTargEval */
  110.  
  111.  
  112. /*****************************************************************************/
  113. /*                                                                           */
  114. /*  static OBJECT CrossGenTag(x)                                             */
  115. /*                                                                           */
  116. /*  Generate a tag suitable for labelling closure x, in such a way that      */
  117. /*  the same tag is likely to be generated on subsequent runs.               */
  118. /*                                                                           */
  119. /*****************************************************************************/
  120.  
  121. static OBJECT CrossGenTag(x)
  122. OBJECT x;
  123. { FULL_CHAR buff[MAX_LINE], *str1, *str2;
  124.   OBJECT sym, cs, gt, res;  FILE_NUM fnum;
  125.   FULL_CHAR *sgt;
  126.   int seq;
  127.   debug1(DCR, D, "CrossGenTag( %s )", SymName(actual(x)));
  128.   sym = actual(x);
  129.   if( cross_sym(sym) == nil )  CrossInit(sym);
  130.   cs = cross_sym(sym);
  131.   fnum = file_num(fpos(x));
  132.   /* ***
  133.   if( fnum != gentag_file(cs) )
  134.   { gentag_file(cs) = fnum;
  135.     gentag_seq(cs)  = 0;
  136.   }
  137.   *** */
  138.   str1 = FullSymName(sym, AsciiToFull("."));
  139.   str2 = FileName(fnum);
  140.   gt = gentag_fseq(cs);
  141.   sgt = string(gt);
  142.   seq = ++(sgt[fnum]);
  143.   if( StringLength(str1) + StringLength(str2) + 10 >= MAX_LINE )
  144.     Error(FATAL,no_fpos, "automatically generated tag \"%s.%s.%d\" is too long",
  145.     str1, str2, seq);
  146.   StringCopy(buff, str1);
  147.   StringCat(buff, AsciiToFull("."));
  148.   StringCat(buff, str2);
  149.   StringCat(buff, AsciiToFull("."));
  150.   StringCat(buff, StringInt(seq));
  151.   res = MakeWord(QWORD, buff, &fpos(x));
  152.   debug1(DCR, DD, "CrossGenTag returning %s", string(res));
  153.   return res;
  154. } /* end CrossGenTag */
  155.  
  156.  
  157. /*@::CrossAddTag()@***********************************************************/
  158. /*                                                                           */
  159. /*  CrossAddTag(x)                                                           */
  160. /*                                                                           */
  161. /*  Add an automatically generated @Tag parameter to closure x if required.  */
  162. /*                                                                           */
  163. /*****************************************************************************/
  164.  
  165. CrossAddTag(x)
  166. OBJECT x;
  167. { OBJECT link, par, ppar, y;
  168.   if( has_tag(actual(x)) )
  169.   { 
  170.     /* search the parameter list of x for a @Tag parameter */
  171.     for( link = Down(x);  link != x;  link = NextDown(link) )
  172.     { Child(par, link);
  173.       if( type(par) == PAR && is_tag(actual(par)) )  break;
  174.     }
  175.     if( link == x )
  176.     { 
  177.       /* search the definition of x for name of its @Tag parameter */
  178.       ppar = nil;
  179.       for( link=Down(actual(x));  link != actual(x);  link = NextDown(link) )
  180.       {    Child(y, link);
  181.     if( is_par(type(y)) && is_tag(y) )
  182.     { ppar = y;
  183.       break;
  184.     }
  185.       }
  186.       if( ppar != nil ) /* should always hold */
  187.       {
  188.     /* prepare new PAR containing generated tag */
  189.     par = New(PAR);
  190.     actual(par) = ppar;
  191.     y = CrossGenTag(x);
  192.     Link(par, y);
  193.  
  194.     /* find the right spot, then link it to x */
  195.     switch( type(ppar) )
  196.     {
  197.       case LPAR:    link = Down(x);
  198.             break;
  199.  
  200.       case NPAR:    link = Down(x);
  201.             if( Down(x) != x )
  202.             { Child(y, Down(x));
  203.               if( type(y) == PAR && type(actual(par)) == LPAR )
  204.                 link = NextDown(link);
  205.             }
  206.             break;
  207.  
  208.       case RPAR:    for( link = Down(x); link != x; link = NextDown(link) )
  209.             { Child(y, link);
  210.               if( type(y) != PAR )  break;
  211.             }
  212.             break;
  213.     }
  214.     Link(link, par);
  215.       }
  216.     }
  217.   }
  218. } /* end CrossAddTag */
  219.  
  220.  
  221. /*@::CrossExpand()@***********************************************************/
  222. /*                                                                           */
  223. /*  OBJECT CrossExpand(x, env, style, crs_wanted, crs, res_env)              */
  224. /*                                                                           */
  225. /*  Return the value of cross-reference x, with environment *res_env.  If x  */
  226. /*  has a non-literal tag, it must be tracked, so an object is added to *crs */
  227. /*  for this purpose if crs_wanted.  Result replaces x, which is disposed.   */
  228. /*                                                                           */
  229. /*****************************************************************************/
  230. static OBJECT nbt[2] = { nil, nil };
  231. static OBJECT nft[2] = { nil, nil };
  232. static OBJECT ntarget = nil;
  233.  
  234. OBJECT CrossExpand(x, env, style, crs_wanted, crs, res_env)
  235. OBJECT x, env;  STYLE *style;  BOOLEAN crs_wanted; OBJECT *crs, *res_env;
  236. { OBJECT sym, res, tag, y, cs, link, db, tmp, index;
  237.   int ctype;  FULL_CHAR buff[MAX_LINE], seq[MAX_LINE], *str;
  238.   FILE_NUM fnum, dfnum;
  239.   long cont, dfpos;
  240.   assert( type(x) == CROSS, "CrossExpand: x!" );
  241.   debug2(DCR, DD, "CrossExpand( %s, %s )", EchoObject(x), EchoObject(*crs));
  242.   assert( NextDown(Down(x)) == LastDown(x), "CrossExpand: #args!" );
  243.  
  244.   /* manifest and tidy the right parameter */
  245.   Child(tag, LastDown(x));
  246.   tag = Manifest(tag, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE);
  247.   tag = ReplaceWithTidy(tag);
  248.  
  249.   /* extract sym (the symbol name) and tag (the tag value) from x */
  250.   Child(y, Down(x));
  251.   if( type(y) == CLOSURE )  sym = actual(y);
  252.   ctype = type(y) != CLOSURE ? 1 :
  253.       !is_word(type(tag)) ? 2 :
  254.       StringEqual(string(tag), STR_EMPTY) ? 3 :
  255.       StringEqual(string(tag), KW_PRECEDING) ? CROSS_PREC :
  256.       StringEqual(string(tag), KW_FOLLOWING) ? CROSS_FOLL : CROSS_LIT;
  257.  
  258.   res = nil;
  259.   switch( ctype )
  260.   {
  261.  
  262.     case 1:
  263.  
  264.       Error(WARN, &fpos(y), "left parameter of %s is not a symbol", KW_CROSS);
  265.       break;
  266.  
  267.  
  268.     case 2:
  269.  
  270.       Error(WARN, &fpos(tag),
  271.     "value of right parameter of %s is not a simple word", KW_CROSS);
  272.       break;
  273.  
  274.  
  275.     case 3:
  276.     
  277.       Error(WARN, &fpos(tag),
  278.     "value of right parameter of %s is an empty word", KW_CROSS);
  279.       break;
  280.  
  281.  
  282.     case CROSS_LIT:
  283.     
  284.       if( cross_sym(sym) == nil )  CrossInit(sym);
  285.       cs = cross_sym(sym);
  286.       if( sym == MomentSym && StringEqual(string(tag), KW_NOW) )
  287.       {    /* this is a request for the current time */
  288.     res = StartMoment();
  289.       }
  290.       else for( link = NextUp(Up(cs));  link != cs;  link = NextUp(link) )
  291.       {    Parent(db, link);
  292.     assert( is_word(type(db)), "CrossExpand: db!" );
  293.     if( DbRetrieve(db, FALSE, sym, string(tag), seq, &dfnum,&dfpos,&cont) )
  294.     { res = ReadFromFile(dfnum, dfpos, sym);
  295.       if( db != OldCrossDb )  AttachEnv(env, res);
  296.       break;
  297.     }
  298.       }
  299.       break;
  300.  
  301.  
  302.     case CROSS_PREC:
  303.     case CROSS_FOLL:
  304.     
  305.       if( cross_sym(sym) == nil )  CrossInit(sym);
  306.       cs = cross_sym(sym);
  307.       assert( cs != nil, "CrossExpand/CROSS_FOLL: cs == nil!" );
  308.       assert( type(cs) == CROSS_SYM, "CrossExpand/CROSS_FOLL: type(cs)!" );
  309.       fnum = file_num(fpos(tag));
  310.       if( fnum != cr_file(cs) )
  311.       {    cr_file(cs) = fnum;
  312.     cr_seq(cs) = 0;
  313.       }
  314.       str = FileName(fnum);
  315.       ++cr_seq(cs);
  316.       if( StringLength(str) + 5 >= MAX_LINE )
  317.     Error(FATAL, &fpos(x), "automatically generated tag %s_%d is too long",
  318.       str, cr_seq(cs));
  319.       StringCopy(buff, str);
  320.       StringCat(buff, AsciiToFull("_"));
  321.       StringCat(buff, StringInt(cr_seq(cs)));
  322.       tmp = CrossMake(sym, MakeWord(WORD, buff, &fpos(tag)), ctype);
  323.       index = New(ctype);
  324.       actual(index) = tmp;
  325.       Link(index, tmp);
  326.       if( crs_wanted )
  327.       {    if( *crs == nil )  *crs = New(CR_LIST);
  328.     link = Link(*crs, index);
  329.       }
  330.       else Error(FATAL, &fpos(x), "%s or %s tag not allowed here",
  331.     KW_PRECEDING, KW_FOLLOWING);
  332.       if( AllowCrossDb &&
  333.       DbRetrieve(OldCrossDb, FALSE, sym, buff, seq, &dfnum, &dfpos,&cont) )
  334.     res = ReadFromFile(dfnum, dfpos, nil);
  335.       break;
  336.  
  337.  
  338.     default:
  339.     
  340.       Error(INTERN, no_fpos, "CrossExpand switch!");
  341.       break;
  342.  
  343.  
  344.   } /* end switch */
  345.   if( res == nil )
  346.   { OBJECT envt;
  347.     if( ctype > 1 )  Error(WARN, &fpos(x), "%s%s%s unknown",
  348.         SymName(sym), KW_CROSS, string(tag));
  349.  
  350.     /* build dummy result with environment attached */
  351.     /* nb at present we are not adding dummy import closures to this! */
  352.     res = New(CLOSURE);  actual(res) = sym;
  353.     y = res;
  354.     debug1(DCR, DD, "First y = %s", SymName(actual(y)));
  355.     while( enclosing(actual(y)) != StartSym )
  356.     { tmp = New(CLOSURE);
  357.       actual(tmp) = enclosing(actual(y));
  358.       debug0(DCR, DD, "  calling SetEnv from CrossExpand (a)");
  359.       envt = SetEnv(tmp, nil);
  360.       AttachEnv(envt, y);
  361.       y = tmp;
  362.       debug1(DCR, DD, "Later y = %s", SymName(actual(y)));
  363.     }
  364.     envt = New(ENV);  Link(y, envt);
  365.   }
  366.  
  367.   /* set environment, replace x by res, debug and exit */
  368.   *res_env = DetachEnv(res);
  369.   ReplaceNode(res, x);
  370.   DisposeObject(x);
  371.   assert( type(res) == CLOSURE, "CrossExpand: type(res) != CLOSURE!" );
  372.   assert( actual(res) == sym, "CrossExpand: actual(res) != sym!" );
  373.   debug1(DCR, DD, "CrossExpand returning %s", EchoObject(res));
  374.   debug1(DCR, DD, "  *crs = %s", EchoObject(*crs));
  375.   debug1(DCR, DD, "  *res_env = %s", EchoObject(*res_env));
  376.   return res;
  377. } /* end CrossExpand */
  378.  
  379.  
  380. /*@::CrossSequence()@*********************************************************/
  381. /*                                                                           */
  382. /*  CrossSequence(x)                                                         */
  383. /*                                                                           */
  384. /*  Object x is an insinuated cross-reference that has just been popped off  */
  385. /*  the top of the root galley.  Resolve it with the sequence of others.     */
  386. /*                                                                           */
  387. /*****************************************************************************/
  388.  
  389. CrossSequence(x)
  390. OBJECT x;
  391. { OBJECT sym, tag, val, tmp, cs, par, key, link, y;
  392.   unsigned ctype;  FULL_CHAR buff[MAX_LINE], *str, *seq;
  393.   FILE_NUM dfnum;  int dfpos;
  394.  
  395.   /* if suppressing cross-referencing, dispose x and quit */
  396.   if( !AllowCrossDb )
  397.   { if( Up(x) == x )  DisposeObject(x);
  398.     debug0(DCR, D, "CrossSequence returning (!AllowCrossDb).");
  399.     return;
  400.   }
  401.  
  402.   /* get interesting fragments from x */
  403.   assert( type(x) == CROSS, "CrossSequence: type(x)!" );
  404.   ctype = cross_type(x);
  405.   Child(tmp, Down(x));
  406.   assert( type(tmp) == CLOSURE, "CrossSequence: type(tmp)!" );
  407.   sym = actual(tmp);
  408.   if( cross_sym(sym) == nil )  CrossInit(sym);
  409.   cs = cross_sym(sym);
  410.   assert( type(cs) == CROSS_SYM, "CrossSequence: cs!" );
  411.  
  412.   /* debug output */
  413.   debug2(DCR, D, "CrossSequence %s %s", Image(ctype), SymName(sym));
  414.   debug1(DCR, DD, "  x = %s", EchoObject(x));
  415.   ifdebug(DCR, DD, DebugObject(cs));
  416.  
  417.   /* delete as much of x as possible */
  418.   Child(tag, NextDown(Down(x)));
  419.   DeleteLink(NextDown(Down(x)));
  420.   if( Up(x) == x )  DisposeObject(x);
  421.  
  422.   switch( ctype )
  423.   {
  424.     case GALL_FOLL:
  425.     case GALL_PREC:
  426.  
  427.       /* find key of the galley, if any */
  428.       val = tag;  key = nil;
  429.       for( link = Down(val);  link != val;  link = NextDown(link) )
  430.       {    Child(par, link);
  431.     if( type(par) == PAR && (is_key(actual(par)) || is_tag(actual(par))) )
  432.     { assert( Down(par) != par, "CrossSequence: PAR child!" );
  433.       Child(key, Down(par));
  434.       key = ReplaceWithTidy(key);
  435.     }
  436.       }
  437.  
  438.       /* write out the galley */
  439.       str = FileName(file_num(fpos(val)));
  440.       dfnum = FileNum(str, DATA_SUFFIX);
  441.       if( dfnum == NO_FILE )
  442.     dfnum = DefineFile(str, DATA_SUFFIX, &fpos(val),
  443.       DATABASE_FILE, SOURCE_PATH);
  444.       AppendToFile(val, dfnum, &dfpos);
  445.  
  446.       /* determine the sequence number or string of this galley */
  447.       if( key == nil )
  448.       {    ++gall_seq(cs);
  449.     StringCopy(buff, StringFiveInt(gall_seq(cs)));
  450.     seq = buff;
  451.       }
  452.       else if( !is_word(type(key)) )
  453.       {    Error(WARN, &fpos(key), "%s parameter is not a word", KW_KEY);
  454.     seq = STR_BADKEY;
  455.       }
  456.       else if( StringEqual(string(key), STR_EMPTY) )
  457.       {    Error(WARN, &fpos(key), "%s parameter is empty word", KW_KEY);
  458.     seq = STR_BADKEY;
  459.       }
  460.       else seq = string(key);
  461.  
  462.       /* either write out the index immediately or store it for later */
  463.       if( ctype == GALL_PREC )
  464.       {    if( gall_tag(cs) == nil )
  465.     { Error(WARN, &fpos(val), "no %s precedes this %s%s%s",
  466.         SymName(sym), SymName(sym), KW_CROSS, KW_PRECEDING);
  467.       debug0(DCR, DD, "  ... so substituting \"none\"");
  468.       gall_tag(cs) = MakeWord(WORD, STR_NONE, &fpos(val));
  469.     }
  470.     assert( is_word(type(gall_tag(cs))) &&
  471.         !StringEqual(string(gall_tag(cs)), STR_EMPTY),
  472.             "CrossSequence: gall_tag!" );
  473.     debug3(DCR, D, "  inserting galley (prec) %s&%s %s", SymName(sym),
  474.       string(gall_tag(cs)), seq);
  475.     DbInsert(NewCrossDb, TRUE, sym, string(gall_tag(cs)), seq,
  476.             dfnum, (long) dfpos);
  477.       }
  478.       else
  479.       {    tmp = MakeWord(WORD, seq, &fpos(val));
  480.     gall_rec(tmp) = TRUE;
  481.     file_num(fpos(tmp)) = dfnum;
  482.     gall_pos(tmp) = dfpos;
  483.     Link(cs, tmp);
  484.     debug2(DCR, D, "  saving galley (foll) %s&? %s", SymName(sym), seq);
  485.       }
  486.       DisposeObject(val);
  487.       break;
  488.  
  489.  
  490.     case GALL_TARG:
  491.  
  492.       if( gall_tag(cs) != nil )  DisposeObject(gall_tag(cs));
  493.       if( !is_word(type(tag)) || StringEqual(string(tag), STR_EMPTY) )
  494.       {
  495.     debug2(DCR, DD, "  GALL_TARG %s put none for %s",
  496.       SymName(sym), EchoObject(tag));
  497.     DisposeObject(tag);
  498.     gall_tag(cs) = MakeWord(WORD, STR_NONE, no_fpos);
  499.       }
  500.       else gall_tag(cs) = tag;
  501.       debug2(DCR, D, "  have new %s gall_targ %s", SymName(sym),
  502.       EchoObject(gall_tag(cs)));
  503.       for( link = Down(cs);  link != cs;  link = NextDown(link) )
  504.       {    Child(y, link);
  505.     assert( is_word(type(y)) && !StringEqual(string(y), STR_EMPTY),
  506.                 "CrossSequence: GALL_TARG y!" );
  507.     if( gall_rec(y) )
  508.     {
  509.       debug3(DCR, D, "  inserting galley (foll) %s&%s %s", SymName(sym),
  510.         string(gall_tag(cs)), string(y));
  511.       DbInsert(NewCrossDb, TRUE, sym, string(gall_tag(cs)), string(y),
  512.             file_num(fpos(y)), (long) gall_pos(y));
  513.       link = PrevDown(link);
  514.       DisposeChild(NextDown(link));
  515.     }
  516.       }
  517.       break;
  518.  
  519.  
  520.     case CROSS_PREC:
  521.  
  522.       if( target_state(cs) == NO_TARGET )
  523.       {    Error(WARN, &fpos(tag), "no invokation of %s precedes this %s%s%s",
  524.         SymName(sym), SymName(sym), KW_CROSS, KW_PRECEDING);
  525.     break;
  526.       }
  527.       if( target_state(cs) == SEEN_TARGET )
  528.       {
  529.     debug2(DCR, D, "  inserting %s cross_targ %s",
  530.       SymName(sym), target_val(cs));
  531.     AppendToFile(target_val(cs), target_file(cs), &target_pos(cs));
  532.     DisposeObject(target_val(cs));
  533.     target_val(cs) = nil;
  534.     target_state(cs) = WRITTEN_TARGET;
  535.       }
  536.       if( !is_word(type(tag)) || StringEqual(string(tag), STR_EMPTY) )
  537.       {
  538.     debug2(DCR, DD, "  GALL_TARG %s put none for %s", SymName(sym),
  539.         EchoObject(tag));
  540.     DisposeObject(tag);
  541.     tag = MakeWord(WORD, STR_NONE, no_fpos);
  542.       }
  543.       debug3(DCR, D, "  inserting cross (prec) %s&%s %s", SymName(sym),
  544.         string(tag), "0");
  545.       DbInsert(NewCrossDb, FALSE, sym, string(tag), STR_ZERO,
  546.     target_file(cs), (long) target_pos(cs));
  547.       DisposeObject(tag);
  548.       break;
  549.  
  550.  
  551.     case CROSS_FOLL:
  552.  
  553.       if( !is_word(type(tag)) )
  554.       {    Error(WARN, &fpos(tag), "tag of %s is not a simple word",
  555.         SymName(symb(cs)));
  556.     debug1(DCR, DD, "  tag = %s", EchoObject(tag));
  557.       }
  558.       else if( StringEqual(string(tag), STR_EMPTY) )
  559.       {
  560.         debug1(DCR, D, "  ignoring cross (foll) %s (empty tag)", SymName(sym));
  561.       }
  562.       else
  563.       { Link(cs, tag);
  564.     gall_rec(tag) = FALSE;
  565.         debug3(DCR, D, "  storing cross (foll) %s&%s %s", SymName(sym),
  566.         string(tag), "?");
  567.       }
  568.       break;
  569.  
  570.  
  571.     case CROSS_TARG:
  572.  
  573.       /* get rid of old target, if any, and add new one */
  574.       if( target_state(cs) == SEEN_TARGET )
  575.       {
  576.     debug2(DCR, D, "  disposing unused %s cross_targ %s", SymName(sym),
  577.       target_val(cs));
  578.     DisposeObject(target_val(cs));
  579.       }
  580.       debug2(DCR, D, "  remembering new %s cross_targ %s", SymName(sym),
  581.     EchoObject(tag));
  582.       target_val(cs) = tag;
  583.       assert( Up(tag) == tag, "CrossSeq: Up(tag)!" );
  584.       str = FileName(file_num(fpos(tag)));
  585.       target_file(cs) = FileNum(str, DATA_SUFFIX);
  586.       if( target_file(cs) == NO_FILE )
  587.     target_file(cs) = DefineFile(str, DATA_SUFFIX, &fpos(tag),
  588.                     DATABASE_FILE, SOURCE_PATH);
  589.       target_state(cs) = SEEN_TARGET;
  590.  
  591.       /* store tag of the galley, if any */
  592.       tag = nil;
  593.       assert( type(target_val(cs)) == CLOSURE, "CrossSequence: target_val!" );
  594.       link = Down(target_val(cs));
  595.       for( ;  link != target_val(cs);  link = NextDown(link) )
  596.       {    Child(par, link);
  597.     if( type(par) == PAR && is_tag(actual(par)) )
  598.     { assert( Down(par) != par, "CrossSequence: Down(PAR)!" );
  599.       Child(tag, Down(par));
  600.       tag = ReplaceWithTidy(tag);
  601.       if( !is_word(type(tag)) )
  602.       { Error(WARN, &fpos(tag), "%s tag is not a simple word",
  603.             SymName(actual(target_val(cs))));
  604.         debug1(DCR, DD, "  tag = %s", EchoObject(tag));
  605.       }
  606.       else if( StringEqual(string(tag), STR_EMPTY) )
  607.       {
  608.             debug1(DCR, D, "  ignoring cross (own tag) %s (empty tag)",
  609.         SymName(sym));
  610.       }
  611.       else
  612.       { Link(cs, tag);
  613.         gall_rec(tag) = FALSE;
  614.             debug3(DCR, D, "  storing cross (own tag) %s&%s %s", SymName(sym),
  615.         string(tag), "?");
  616.       }
  617.       break;
  618.     }
  619.       }
  620.  
  621.       /* if new target is already writable, write it */
  622.       if( Down(cs) != cs )
  623.       {
  624.     debug2(DCR, D, "  writing %s cross_targ %s", SymName(sym),
  625.         EchoObject(target_val(cs)));
  626.     AppendToFile(target_val(cs), target_file(cs), &target_pos(cs));
  627.     DisposeObject(target_val(cs));
  628.     for( link = Down(cs);  link != cs;  link = NextDown(link) )
  629.     { Child(tag, link);
  630.       assert( is_word(type(tag)) && !StringEqual(string(tag), STR_EMPTY),
  631.             "CrossSeq: non-WORD or empty tag!" );
  632.       if( !gall_rec(tag) )
  633.       {
  634.         debug3(DCR, D, "  inserting cross (foll) %s&%s %s", SymName(sym),
  635.           string(tag), "0");
  636.         DbInsert(NewCrossDb, FALSE, sym, string(tag),
  637.           STR_ZERO, target_file(cs), (long) target_pos(cs));
  638.         link = PrevDown(link);
  639.         DisposeChild(NextDown(link));
  640.       }
  641.     }
  642.     target_state(cs) = WRITTEN_TARGET;
  643.       }
  644.       break;
  645.  
  646.  
  647.     default:
  648.  
  649.       Error(INTERN, &fpos(tag), "CrossSequence: ctype = %s", Image(ctype));
  650.       break;
  651.  
  652.   } /* end switch */
  653.   debug0(DCR, D, "CrossSequence returning.");
  654.   debug0(DCR, DD, "   cs =");
  655.   ifdebug(DCR, DD, DebugObject(cs));
  656. } /* end CrossSequence */
  657.  
  658.  
  659. /*@::CrossClose()@************************************************************/
  660. /*                                                                           */
  661. /*  CrossClose()                                                             */
  662. /*                                                                           */
  663. /*  Check for dangling forward references, and convert old cross reference   */
  664. /*  database to new one.                                                     */
  665. /*                                                                           */
  666. /*****************************************************************************/
  667.  
  668. CrossClose()
  669. { OBJECT link, cs, ylink, y, sym;  BOOLEAN g;  int len, count;
  670.   FILE_NUM dfnum;  long dfpos, cont;
  671.   FULL_CHAR buff[MAX_LINE], seq[MAX_LINE], tag[MAX_LINE];
  672.   debug0(DCR, D, "CrossClose()");
  673.   ifdebug(DCR, DD, if( RootCross != nil ) DebugObject(RootCross));
  674.  
  675.   /* if suppressing cross referencing, return */
  676.   if( !AllowCrossDb )
  677.   { debug0(DCR, D, "CrossClose returning (!AllowCrossDb).");
  678.     return;
  679.   }
  680.  
  681.   /* check for dangling forward references and dispose cross ref structures */
  682.   if( RootCross != nil )
  683.   { for( link = Down(RootCross);  link != RootCross;  link = NextDown(link) )
  684.     { Child(cs, link);
  685.       assert( type(cs) == CROSS_SYM, "CrossClose: type(cs)!" );
  686.       count = 0;  ylink = Down(cs);
  687.       while( ylink != cs && count <= 5 )
  688.       {    Child(y, ylink);
  689.     Error(WARN, &fpos(y), "no invokation of %s follows this %s%s%s",
  690.       SymName(symb(cs)), SymName(symb(cs)), KW_CROSS, KW_FOLLOWING);
  691.     debug2(DCR, D, "gall_rec(y) = %s, y = %s",
  692.       bool(gall_rec(y)), EchoObject(y));
  693.     if( gall_rec(y) )
  694.       DbInsert(NewCrossDb, TRUE, symb(cs), STR_NONE,
  695.         string(y), file_num(fpos(y)), (long) gall_pos(y));
  696.     count++;  ylink = NextDown(ylink);
  697.       }
  698.       if( ylink != cs )  Error(WARN, no_fpos, "and more undefined %s%s%s",
  699.                 SymName(symb(cs)), KW_CROSS, KW_FOLLOWING);
  700.       ifdebug(ANY, D,
  701.     if( target_state(cs) == SEEN_TARGET )  DisposeObject(target_val(cs));
  702.     if( gall_tag(cs) != nil )  DisposeObject(gall_tag(cs));
  703.       );
  704.     }
  705.     ifdebug(ANY, D, DisposeObject(RootCross); );
  706.   }
  707.  
  708.   /* add to NewCrossDb those entries of OldCrossDb from other source files */
  709.   cont = 0L;  len = StringLength(DATA_SUFFIX);
  710.   while( DbRetrieveNext(OldCrossDb, &g, &sym, tag, seq, &dfnum, &dfpos, &cont) )
  711.   { if( g ) continue;
  712.     StringCopy(buff, FileName(dfnum));
  713.     StringCopy(&buff[StringLength(buff) - len], STR_EMPTY);
  714.     if( FileNum(buff, STR_EMPTY) == NO_FILE )
  715.       DbInsert(NewCrossDb, FALSE, sym, tag, seq, dfnum, dfpos);
  716.   }
  717.  
  718.   /* close OldCrossDb's .li file so that NewCrossDb can use its name */
  719.   DbClose(OldCrossDb);
  720.  
  721.   /* make NewCrossDb readable, for next run */
  722.   DbConvert(NewCrossDb, TRUE);
  723.  
  724.   debug0(DCR, D, "CrossClose returning.");
  725. } /* end CrossClose */
  726.