blob: a96be012d71ec5bd06f76319d20d2053c233a18c [file] [log] [blame]
/*@z10.c:Cross References:CrossInit(), CrossMake()@***************************/
/* */
/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.24) */
/* COPYRIGHT (C) 1991, 2000 Jeffrey H. Kingston */
/* */
/* Jeffrey H. Kingston (jeff@cs.usyd.edu.au) */
/* Basser Department of Computer Science */
/* The University of Sydney 2006 */
/* AUSTRALIA */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either Version 2, or (at your option) */
/* any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA */
/* */
/* FILE: z10.c */
/* MODULE: Cross References */
/* EXTERNS: CrossInit(), CrossMake(), GallTargEval(), CrossAddTag(), */
/* CrossExpand(), CrossSequence(), CrossClose() */
/* */
/*****************************************************************************/
#include "externs.h"
#define NO_TARGET 0
#define SEEN_TARGET 1
#define WRITTEN_TARGET 2
#define INIT_CROSSREF_NUM 100
static OBJECT RootCross = nilobj; /* header for all crs */
/*****************************************************************************/
/* */
/* CROSSREF_TABLE */
/* */
/* A symbol table permitting access to cross reference generated tags by */
/* a mapping (symbol x file) -> current tag. */
/* */
/* crtab_getnext(sym, fnum, S) Get next value associated with sym,fnum */
/* crtab_debug(S, fp) Debug print of table S to file fp */
/* */
/*****************************************************************************/
typedef struct crossref_rec
{ struct crossref_rec *crtab_next;
OBJECT crtab_sym;
FILE_NUM crtab_fnum;
int crtab_value;
} *CROSSREF_ENTRY;
typedef struct
{ int tab_size; /* size of table */
int tab_count; /* number of entries held */
CROSSREF_ENTRY tab_chains[1]; /* the chains of entries */
} *CROSSREF_TABLE;
#define crtab_size(S) (S)->tab_size
#define crtab_count(S) (S)->tab_count
#define crtab_chain(S,i) (S)->tab_chains[i]
#define hash(pos, sym, fnum, S) \
{ pos = ( ((unsigned long) sym) + fnum ) % crtab_size(S); \
}
static CROSSREF_TABLE crtab_new(int newsize)
{ CROSSREF_TABLE S; int i;
ifdebug(DMA, D, DebugRegisterUsage(MEM_CROSSREF, 1,
2*sizeof(int) + newsize*sizeof(CROSSREF_ENTRY)));
S = (CROSSREF_TABLE)
malloc(2*sizeof(int) + newsize*sizeof(CROSSREF_ENTRY));
if( S == (CROSSREF_TABLE) NULL )
Error(10, 1, "run out of memory enlarging crossref table", FATAL, no_fpos);
crtab_size(S) = newsize;
crtab_count(S) = 0;
for( i = 0; i < newsize; i++ )
crtab_chain(S, i) = (CROSSREF_ENTRY) nilobj;
return S;
} /* end crtab_new */
static CROSSREF_TABLE crtab_rehash(CROSSREF_TABLE S, int newsize)
{ CROSSREF_TABLE NewS; int i; unsigned long newpos; CROSSREF_ENTRY p, q;
NewS = crtab_new(newsize);
for( i = 0; i < crtab_size(S); i++ )
{ p = crtab_chain(S, i);
while( p != NULL )
{ q = p->crtab_next;
hash(newpos, p->crtab_sym, p->crtab_fnum, NewS);
p->crtab_next = crtab_chain(NewS, newpos);
crtab_chain(NewS, newpos) = p;
crtab_count(NewS)++;
p = q;
}
}
ifdebug(DMA, D, DebugRegisterUsage(MEM_CROSSREF, -1,
-(2*sizeof(int) + crtab_size(S)*sizeof(CROSSREF_ENTRY))));
free(S);
return NewS;
} /* end crtab_rehash */
static int crtab_getnext(OBJECT sym, FILE_NUM fnum, CROSSREF_TABLE *S)
{ CROSSREF_ENTRY x; OBJECT t; unsigned long pos;
/* if S is NULL, create a new table */
if( *S == NULL ) *S = crtab_new(INIT_CROSSREF_NUM);
/* if (sym, fnum) exists, increment its value and return it */
hash(pos, sym, fnum, *S);
for( x = crtab_chain(*S, pos); x != NULL; x = x->crtab_next )
{ if( x->crtab_sym == sym && x->crtab_fnum == fnum )
return ++x->crtab_value;
}
/* if table is full, rehash */
if( crtab_count(*S) == crtab_size(*S) )
{ *S = crtab_rehash(*S, 2*crtab_size(*S));
hash(pos, sym, fnum, *S);
}
/* insert a new entry for (sym, fnum) with value 1 */
GetMem(t, sizeof(struct crossref_rec), no_fpos);
x = (CROSSREF_ENTRY) t;
x->crtab_sym = sym;
x->crtab_fnum = fnum;
x->crtab_next = crtab_chain(*S, pos);
crtab_chain(*S, pos) = x;
crtab_count(*S)++;
return x->crtab_value = 1;
} /* end crtab_getnext */
#if DEBUG_ON
static void crtab_debug(CROSSREF_TABLE S, FILE *fp)
{ int i; CROSSREF_ENTRY x;
if( S == NULL )
{ fprintf(fp, " null table\n");
return;
}
fprintf(fp, " table size: %d; current count: %d\n",
crtab_size(S), crtab_count(S));
for( i = 0; i < crtab_size(S); i++ )
{ fprintf(fp, "crtab_chain(S, %d) =", i);
for( x = crtab_chain(S, i); x != NULL; x = x->crtab_next )
{ fprintf(fp, " %s:%s,%d",
SymName(x->crtab_sym), FileName(x->crtab_fnum), x->crtab_value);
}
fprintf(fp, "\n");
}
} /* end crtab_debug */
#endif
static CROSSREF_TABLE crossref_tab = NULL;
/*@@**************************************************************************/
/* */
/* CrossInit(sym) Initialize cross_sym(sym). */
/* */
/*****************************************************************************/
void CrossInit(OBJECT sym)
{ OBJECT cs;
New(cs, CROSS_SYM);
target_state(cs) = NO_TARGET; target_seq(cs) = 0;
/* cr_file(cs) = NO_FILE; unused */
gall_seq(cs) = 0; gall_tag(cs) = nilobj;
gall_tfile(cs) = NO_FILE;
symb(cs) = sym; cross_sym(sym) = cs;
if( RootCross == nilobj ) New(RootCross, CR_ROOT); Link(RootCross, cs);
}
/*****************************************************************************/
/* */
/* OBJECT CrossMake(sym, val, ctype) */
/* */
/* Make a cross-reference with the given sym and tag value (NB no fpos). */
/* */
/*****************************************************************************/
OBJECT CrossMake(OBJECT sym, OBJECT val, int ctype)
{ OBJECT v1, res;
debug3(DCR, DD, "CrossMake(%s, %s, %s)", SymName(sym),
EchoObject(val), Image(ctype));
New(res, CROSS); cross_type(res) = ctype; threaded(res) = FALSE;
New(v1, CLOSURE); actual(v1) = sym;
Link(res, v1); Link(res, val);
debug1(DCR, DD, "CrossMake returning %s", EchoObject(res));
return res;
}
/*@::GallTargEval(), CrossGenTag()@*******************************************/
/* */
/* OBJECT GallTargEval(sym, dfpos) */
/* */
/* Produce a suitable cross-reference for a galley target. */
/* */
/*****************************************************************************/
OBJECT GallTargEval(OBJECT sym, FILE_POS *dfpos)
{ OBJECT cs, res;
FULL_CHAR buff[MAX_BUFF], *str;
debug2(DCR, DD, "GallTargEval( %s,%s )", SymName(sym), EchoFilePos(dfpos));
if( cross_sym(sym) == nilobj ) CrossInit(sym);
cs = cross_sym(sym);
if( file_num(*dfpos) != gall_tfile(cs) )
{ gall_tfile(cs) = file_num(*dfpos);
gall_seq(cs) = 0;
}
str = FileName(gall_tfile(cs));
++gall_seq(cs);
if( StringLength(str) + 6 >= MAX_BUFF )
Error(10, 2, "automatically generated tag %s&%d is too long",
FATAL, dfpos, str, gall_seq(cs));
StringCopy(buff, str);
StringCat(buff, AsciiToFull("&"));
StringCat(buff, StringInt(gall_seq(cs)));
res = CrossMake(sym, MakeWord(WORD, buff, dfpos), GALL_TARG);
debug1(DCR, DD, "GallTargEval returning %s", EchoObject(res));
return res;
} /* end GallTargEval */
/*****************************************************************************/
/* */
/* static OBJECT CrossGenTag(x) */
/* */
/* Generate a tag suitable for labelling closure x, in such a way that */
/* the same tag is likely to be generated on subsequent runs. */
/* */
/*****************************************************************************/
static OBJECT CrossGenTag(OBJECT x)
{ FULL_CHAR buff[MAX_BUFF], *file_name;
OBJECT sym, res; FILE_NUM fnum;
int seq;
debug1(DCR, DD, "CrossGenTag( %s )", SymName(actual(x)));
sym = actual(x);
if( cross_sym(sym) == nilobj ) CrossInit(sym);
fnum = file_num(fpos(x));
file_name = FileName(fnum);
seq = crtab_getnext(sym, fnum, &crossref_tab);
debug3(DCR, DDD, "%d = crtab_getnext(%s, %s, S); S =",
seq, SymName(sym), FileName(fnum));
ifdebug(DCR, DDD, crtab_debug(crossref_tab, stderr));
if( StringLength(file_name) + 20 >= MAX_BUFF )
Error(10, 3, "automatically generated tag is too long (contains %s)",
FATAL, &fpos(x), file_name);
sprintf( (char *) buff, "%d.%d.%s.%d",
file_num(fpos(sym)), line_num(fpos(sym)), file_name, seq);
res = MakeWord(QWORD, buff, &fpos(x));
debug2(DCR, DD, "CrossGenTag( %s ) returning %s", SymName(actual(x)), string(res));
return res;
} /* end CrossGenTag */
/*@::CrossAddTag()@***********************************************************/
/* */
/* CrossAddTag(x) */
/* */
/* Add an automatically generated @Tag parameter to closure x if required. */
/* */
/*****************************************************************************/
void CrossAddTag(OBJECT x)
{ OBJECT link, par, ppar, y;
debug1(DCR, DD, "CrossAddTag( %s )", EchoObject(x));
/* search the parameter list of x for a @Tag parameter */
for( link = Down(x); link != x; link = NextDown(link) )
{ Child(par, link);
if( type(par) == PAR && is_tag(actual(par)) )
{
/* has tag, but if value is empty object, delete it */
Child(y, Down(par));
if( is_word(type(y)) && StringEqual(string(y), STR_EMPTY) )
{ DisposeChild(link);
link = x;
}
break;
}
}
if( link == x )
{
/* search the definition of x for name of its @Tag parameter */
ppar = nilobj;
for( link=Down(actual(x)); link != actual(x); link = NextDown(link) )
{ Child(y, link);
if( is_par(type(y)) && is_tag(y) )
{ ppar = y;
break;
}
}
if( ppar != nilobj ) /* should always hold */
{
/* prepare new PAR containing generated tag */
New(par, PAR);
actual(par) = ppar;
y = CrossGenTag(x);
Link(par, y);
/* find the right spot, then link it to x */
switch( type(ppar) )
{
case LPAR: link = Down(x);
break;
case NPAR: link = Down(x);
if( Down(x) != x )
{ Child(y, Down(x));
if( type(y) == PAR && type(actual(y)) == LPAR )
link = NextDown(link);
}
break;
case RPAR: for( link = Down(x); link != x; link = NextDown(link) )
{ Child(y, link);
if( type(y) != PAR ) break;
}
break;
}
Link(link, par);
}
}
debug1(DCR, DD, "CrossAddTag returning %s", EchoObject(x));
} /* end CrossAddTag */
/*@::CrossExpand()@***********************************************************/
/* */
/* OBJECT CrossExpand(x, env, style, crs, res_env) */
/* */
/* Return the value of cross-reference x, with environment *res_env. If */
/* x has a non-literal tag, it must be tracked, so an object is added to */
/* *crs for this purpose. The result replaces x, which is disposed. */
/* */
/*****************************************************************************/
static OBJECT nbt[2] = { nilobj, nilobj };
static OBJECT nft[2] = { nilobj, nilobj };
static OBJECT ntarget = nilobj;
static OBJECT nenclose = nilobj;
OBJECT CrossExpand(OBJECT x, OBJECT env, STYLE *style,
OBJECT *crs, OBJECT *res_env)
{ OBJECT sym, res, tag, y, cs, link, db, tmp, index;
int ctype, count, i; FULL_CHAR buff[MAX_BUFF], seq[MAX_BUFF], *str;
FILE_NUM fnum, dfnum; BOOLEAN tagerror = FALSE;
long cont, dfpos; int dlnum;
assert( is_cross(type(x)), "CrossExpand: x!" );
debug2(DCR, DD, "[ CrossExpand( %s, env, style, %s, res_env )",
EchoObject(x), EchoObject(*crs));
assert( NextDown(Down(x)) == LastDown(x), "CrossExpand: #args!" );
/* manifest and tidy the right parameter */
Child(tag, LastDown(x));
debug0(DOM, D, " [ calling Manifest from CrossExpand");
tag = Manifest(tag, env, style, nbt, nft, &ntarget, crs, FALSE, FALSE, &nenclose, FALSE);
debug0(DOM, D, " ] returning from Manifest");
tag = ReplaceWithTidy(tag, TRUE); /* && */
/* extract sym (the symbol name) and tag (the tag value) from x */
Child(y, Down(x));
assert( type(y) == CLOSURE, "ClosureExpand: type(y) != CLOSURE!" );
sym = actual(y);
ctype = !is_word(type(tag)) ? 1 :
StringEqual(string(tag), STR_EMPTY) ? 2 :
StringEqual(string(tag), KW_PRECEDING) ? CROSS_PREC :
StringEqual(string(tag), KW_FOLL_OR_PREC) ? CROSS_FOLL_OR_PREC :
StringEqual(string(tag), KW_FOLLOWING) ? CROSS_FOLL : CROSS_LIT;
res = nilobj;
switch( ctype )
{
case 1:
Error(10, 4, "value of right parameter of %s is not a simple word",
WARN, &fpos(tag), KW_CROSS);
break;
case 2:
Error(10, 5, "value of right parameter of %s is an empty word",
WARN, &fpos(tag), KW_CROSS);
break;
case CROSS_LIT:
debug2(DCR, DD, " CROSS_LIT sym %s, tag %s", SymName(sym), string(tag));
if( cross_sym(sym) == nilobj ) CrossInit(sym);
cs = cross_sym(sym);
if( sym == MomentSym && StringEqual(string(tag), KW_NOW) )
{ /* this is a request for the current time */
res = StartMoment();
}
else
{ if( !has_tag(sym) )
{ Error(10, 6, "symbol %s used in cross reference has no %s parameter",
WARN, &fpos(x), SymName(sym), KW_TAG);
tagerror = TRUE;
}
for( link = NextUp(Up(cs)); link != cs; link = NextUp(link) )
{ Parent(db, link);
assert( is_word(type(db)), "CrossExpand: db!" );
if( DbRetrieve(db, FALSE, sym, string(tag), seq, &dfnum, &dfpos,
&dlnum, &cont) )
{
SwitchScope(nilobj);
count = 0;
/* condition db != OldCrossDb added to fix inconsistency with */
/* the call to AttachEnv below, which always carried it; but */
/* there may still be a problem when db != OldCrossDb because */
/* in that case all symbols currently visible are declared */
/* visible in the database entry; perhaps InitialEnvironment */
/* would be best */
if( db != OldCrossDb )
{ SetScope(env, &count, FALSE);
debug2(DCR, DD, "Retrieving %s, env = %s", SymName(sym),
EchoObject(env));
}
else
{ debug1(DCR, DD, "Retrieving %s, env = nilobj", SymName(sym));
}
res = ReadFromFile(dfnum, dfpos, dlnum);
for( i = 1; i <= count; i++ ) PopScope();
UnSwitchScope(nilobj);
if( db != OldCrossDb ) AttachEnv(env, res);
break;
}
}
}
break;
case CROSS_PREC:
case CROSS_FOLL:
case CROSS_FOLL_OR_PREC:
if( has_tag(sym) )
{ int new_seq;
if( cross_sym(sym) == nilobj ) CrossInit(sym);
cs = cross_sym(sym);
assert( cs != nilobj, "CrossExpand/CROSS_FOLL: cs == nilobj!" );
assert( type(cs) == CROSS_SYM, "CrossExpand/CROSS_FOLL: type(cs)!" );
/* generate literal tag buff, used to track this cross reference */
fnum = file_num(fpos(tag));
new_seq = crtab_getnext(sym, fnum, &crossref_tab);
str = FileName(fnum);
if( StringLength(str) + 5 >= MAX_BUFF )
Error(10, 7, "automatically generated tag %s_%d is too long",
FATAL, &fpos(x), str, new_seq); /* was cr_seq(cs) */
StringCopy(buff, str);
StringCat(buff, AsciiToFull("_"));
StringCat(buff, StringInt(new_seq)); /* was cr_seq(cs) */
debug1(DCR, DD, " CROSS_PREC or CROSS_FOLL generated tag %s", buff);
/* generate tracking cross reference and index, and add to *crs */
tmp = CrossMake(sym, MakeWord(WORD, buff, &fpos(tag)), ctype);
New(index, ctype);
actual(index) = tmp;
Link(index, tmp);
if( *crs == nilobj ) New(*crs, CR_LIST);
Link(*crs, index);
/* read tracking cross ref from previous run from cross-ref database */
if( AllowCrossDb &&
DbRetrieve(OldCrossDb, FALSE, sym, buff, seq, &dfnum, &dfpos,
&dlnum, &cont) )
{
SwitchScope(nilobj);
res = ReadFromFile(dfnum, dfpos, dlnum);
UnSwitchScope(nilobj);
}
}
else
{ Error(10, 8, "symbol %s used in cross reference has no %s parameter",
WARN, &fpos(x), SymName(sym), KW_TAG);
tagerror = TRUE;
}
break;
default:
assert(FALSE, "CrossExpand ctype");
break;
} /* end switch */
if( res == nilobj )
{ OBJECT envt;
/* *** reporting this now whether or not crs_wanted
if( ctype > 1 && !tagerror && crs_wanted )
*** */
if( ctype > 1 && !tagerror )
{ debug3(DCR, DD, " reporting unresolved cross reference %s%s%s",
SymName(sym), KW_CROSS, string(tag));
Error(10, 9, "unresolved cross reference %s%s%s",
WARN, &fpos(x), SymName(sym), KW_CROSS, string(tag));
}
/* build dummy result with environment attached */
/* nb at present we are not adding dummy import closures to this! */
New(res, CLOSURE); actual(res) = sym;
y = res;
debug1(DCR, DD, "First y = %s", SymName(actual(y)));
while( enclosing(actual(y)) != StartSym )
{ New(tmp, CLOSURE);
actual(tmp) = enclosing(actual(y));
debug0(DCR, DDD, " calling SetEnv from CrossExpand (a)");
envt = SetEnv(tmp, nilobj);
AttachEnv(envt, y);
y = tmp;
debug1(DCR, DD, "Later y = %s", SymName(actual(y)));
}
New(envt, ENV); Link(y, envt);
}
/* set environment, replace x by res, debug and exit */
*res_env = DetachEnv(res);
ReplaceNode(res, x);
DisposeObject(x);
assert( type(res) == CLOSURE, "CrossExpand: type(res) != CLOSURE!" );
assert( actual(res) == sym, "CrossExpand: actual(res) != sym!" );
debug1(DCR, DD, "] CrossExpand returning %s", EchoObject(res));
debug1(DCR, DD, " *crs = %s", EchoObject(*crs));
debug1(DCR, DD, " *res_env = %s", EchoObject(*res_env));
return res;
} /* end CrossExpand */
/*@::CrossSequence()@*********************************************************/
/* */
/* CrossSequence(x) */
/* */
/* Object x is an insinuated cross-reference that has just been popped off */
/* the top of the root galley. Resolve it with the sequence of others. */
/* */
/*****************************************************************************/
void CrossSequence(OBJECT x)
{ OBJECT sym, tag, val, tmp, cs, par, key, hold_key, link, y, env, hold_env;
unsigned ctype; FULL_CHAR buff[MAX_BUFF], *seq;
FILE_NUM dfnum; int dfpos, dlnum;
/* if suppressing cross-referencing, dispose x and quit */
if( !AllowCrossDb )
{ if( Up(x) == x ) DisposeObject(x);
debug0(DCR, DD, "CrossSequence returning (!AllowCrossDb).");
return;
}
/* get interesting fragments from x */
debugcond1(DCR, DD, !is_cross(type(x)), " type(x) = %s, x =", Image(type(x)));
ifdebugcond(DCR, DD, !is_cross(type(x)), DebugObject(x));
assert( is_cross(type(x)), "CrossSequence: type(x)!" );
ctype = cross_type(x);
Child(tmp, Down(x));
assert( type(tmp) == CLOSURE, "CrossSequence: type(tmp)!" );
sym = actual(tmp);
if( cross_sym(sym) == nilobj ) CrossInit(sym);
cs = cross_sym(sym);
assert( type(cs) == CROSS_SYM, "CrossSequence: cs!" );
/* debug output */
debug2(DCR, D, "[ CrossSequence %s %s", Image(ctype), EchoObject(x));
debug1(DCR, DD, " x = %s", EchoObject(x));
ifdebug(DCR, D, DebugObject(cs));
/* delete as much of x as possible */
Child(tag, NextDown(Down(x)));
DeleteLink(NextDown(Down(x)));
if( Up(x) == x ) DisposeObject(x);
switch( ctype )
{
case GALL_FOLL:
case GALL_FOLL_OR_PREC:
case GALL_PREC:
/* find the value of key of the galley, if any */
val = tag; key = hold_key = nilobj;
assert( type(val) == CLOSURE, "CrossSequence/GALL_FOLL: type(val)!" );
if( has_key(actual(val)) )
{ for( link=Down(actual(val)); link != actual(val); link=NextDown(link) )
{ Child(y, link);
if( is_key(y) )
{ OBJECT nbt[2], nft[2], crs, ntarget, nenclose;
nbt[COLM] = nft[COLM] = nbt[ROWM] = nft[ROWM] = nilobj;
crs = ntarget = nenclose = nilobj;
New(key, CLOSURE);
actual(key) = y;
New(hold_key, ACAT);
Link(hold_key, key);
New(env, ENV);
Link(env, val);
New(hold_env, ACAT);
Link(hold_env, env);
debug0(DOM, D, " [ calling Manifest from CrossSequence");
key = Manifest(key, env, &save_style(val), nbt, nft,
&ntarget, &crs, FALSE, TRUE, &nenclose, FALSE);
debug0(DOM, D, " ] returning from Manifest");
key = ReplaceWithTidy(key, TRUE);
DeleteLink(Down(env));
DisposeObject(hold_env);
}
}
}
/* write out the galley */
dfnum = DatabaseFileNum(&fpos(val));
AppendToFile(val, dfnum, &dfpos, &dlnum);
/* determine the sequence number or string of this galley */
if( key == nilobj )
{ ++gall_seq(cs);
StringCopy(buff, StringFiveInt(gall_seq(cs)));
seq = buff;
}
else if( !is_word(type(key)) )
{ Error(10, 10, "%s parameter is not a word", WARN, &fpos(key), KW_KEY);
debug1(DCR, DD, "key = %s", EchoObject(key));
seq = STR_BADKEY;
}
else if( StringEqual(string(key), STR_EMPTY) )
{ Error(10, 11, "%s parameter is an empty word", WARN,&fpos(key),KW_KEY);
seq = STR_BADKEY;
}
else seq = string(key);
/* either write out the index immediately or store it for later */
/* if( ctype == GALL_PREC || ctype == GALL_FOLL_OR_PREC ) */
if( ctype == GALL_PREC )
{ if( gall_tag(cs) == nilobj )
{
if( ctype == GALL_PREC )
Error(10, 12, "no %s galley target precedes this %s%s%s", WARN,
&fpos(val), SymName(sym), SymName(sym), KW_CROSS, KW_PRECEDING);
else
Error(10, 22, "no %s galley target follows or precedes this %s%s%s",
WARN, &fpos(val), SymName(sym), SymName(sym), KW_CROSS,
KW_FOLL_OR_PREC);
debug0(DCR, DD, " ... so substituting \"none\"");
gall_tag(cs) = MakeWord(WORD, STR_NONE, &fpos(val));
}
assert( is_word(type(gall_tag(cs))) &&
!StringEqual(string(gall_tag(cs)), STR_EMPTY),
"CrossSequence: gall_tag!" );
debug4(DCR, DD, " inserting galley (%s) %s&%s %s",
ctype == GALL_PREC ? "GALL_PREC" : "GALL_FOLL_OR_PREC", SymName(sym),
string(gall_tag(cs)), seq);
DbInsert(NewCrossDb, TRUE, sym, string(gall_tag(cs)), no_fpos, seq,
dfnum, (long) dfpos, dlnum, FALSE);
}
else
{ tmp = MakeWord(WORD, seq, &fpos(val));
cs_type(tmp) = ctype;
cs_fnum(tmp) = dfnum;
cs_pos(tmp) = dfpos;
cs_lnum(tmp) = dlnum;
Link(cs, tmp);
debug2(DCR, D, " saving galley (foll) %s&? %s", SymName(sym), seq);
}
DisposeObject(val);
if( hold_key != nilobj ) DisposeObject(hold_key);
break;
case GALL_TARG:
if( gall_tag(cs) != nilobj ) DisposeObject(gall_tag(cs));
if( !is_word(type(tag)) || StringEqual(string(tag), STR_EMPTY) )
{
debug2(DCR, D, " GALL_TARG %s put none for %s",
SymName(sym), EchoObject(tag));
DisposeObject(tag);
gall_tag(cs) = MakeWord(WORD, STR_NONE, no_fpos);
}
else gall_tag(cs) = tag;
debug2(DCR, D, " have new %s gall_targ %s", SymName(sym),
EchoObject(gall_tag(cs)));
for( link = Down(cs); link != cs; link = NextDown(link) )
{ Child(y, link);
assert( is_word(type(y)) && !StringEqual(string(y), STR_EMPTY),
"CrossSequence: GALL_TARG y!" );
switch( cs_type(y) )
{
case GALL_PREC:
case GALL_FOLL:
case GALL_FOLL_OR_PREC:
debug4(DCR, D, " inserting galley (%s) %s&%s %s",
Image(cs_type(y)), SymName(sym), string(gall_tag(cs)), string(y));
if( Down(y) != y )
Child(val, Down(y));
else
val = nilobj;
DbInsert(NewCrossDb, TRUE, sym, string(gall_tag(cs)), no_fpos,
string(y), cs_fnum(y), (long) cs_pos(y), cs_lnum(y), FALSE);
link = PrevDown(link);
DisposeChild(NextDown(link));
break;
case CROSS_LIT:
case CROSS_PREC:
case CROSS_FOLL:
case CROSS_FOLL_OR_PREC:
break;
default:
assert(FALSE, "CrossSequence: cs_type!");
break;
}
}
break;
case CROSS_PREC:
if( target_state(cs) == NO_TARGET )
{ Error(10, 13, "no %s precedes this %s%s%s", WARN, &fpos(tag),
SymName(sym), SymName(sym), KW_CROSS, KW_PRECEDING);
break;
}
if( target_state(cs) == SEEN_TARGET )
{
debug2(DCR, DD, " inserting %s cross_targ %s",
SymName(sym), target_val(cs));
AppendToFile(target_val(cs), target_file(cs), &target_pos(cs),
&target_lnum(cs));
DisposeObject(target_val(cs));
target_val(cs) = nilobj;
target_state(cs) = WRITTEN_TARGET;
}
if( !is_word(type(tag)) || StringEqual(string(tag), STR_EMPTY) )
{
debug2(DCR, DD, " GALL_TARG %s put none for %s", SymName(sym),
EchoObject(tag));
DisposeObject(tag);
tag = MakeWord(WORD, STR_NONE, no_fpos);
}
debug3(DCR, DD, " inserting cross (prec) %s&%s %s", SymName(sym),
string(tag), "0");
DbInsert(NewCrossDb, FALSE, sym, string(tag), &fpos(tag), STR_ZERO,
target_file(cs), (long) target_pos(cs), target_lnum(cs), TRUE);
DisposeObject(tag);
break;
case CROSS_FOLL:
case CROSS_FOLL_OR_PREC:
if( !is_word(type(tag)) )
{ Error(10, 14, "tag of %s is not a simple word",
WARN, &fpos(tag), SymName(symb(cs)));
debug1(DCR, DD, " tag = %s", EchoObject(tag));
}
else if( StringEqual(string(tag), STR_EMPTY) )
{
debug1(DCR, DD, " ignoring cross (foll) %s (empty tag)", SymName(sym));
}
else
{ Link(cs, tag);
cs_fnum(tag) = file_num(fpos(tag));
cs_type(tag) = ctype;
debug4(DCR, DD, " storing cross (%s) %s&%s %s", Image(ctype),
SymName(sym), string(tag), "?");
}
break;
case CROSS_TARG:
/* get rid of old target, if any, and add new one */
if( target_state(cs) == SEEN_TARGET )
{
debug2(DCR, DD, " disposing unused %s cross_targ %s", SymName(sym),
target_val(cs));
DisposeObject(target_val(cs));
}
debug2(DCR, DD, " remembering new %s cross_targ %s", SymName(sym),
EchoObject(tag));
target_val(cs) = tag;
assert( Up(tag) == tag, "CrossSeq: Up(tag)!" );
target_file(cs) = DatabaseFileNum(&fpos(tag));
target_state(cs) = SEEN_TARGET;
/* store tag of the galley, if any, and delete excessive right pars */
tag = nilobj;
assert( type(target_val(cs)) == CLOSURE, "CrossSequence: target_val!" );
link = Down(target_val(cs));
for( ; link != target_val(cs); link = NextDown(link) )
{ Child(par, link);
if( type(par) == PAR )
{
assert( Down(par) != par, "CrossSequence: Down(PAR)!" );
if( is_tag(actual(par)) )
{
/* sort out the value of this tag now */
Child(tag, Down(par));
tag = ReplaceWithTidy(tag, TRUE); /* && */
if( !is_word(type(tag)) )
{ Error(10, 15, "tag of %s is not a simple word",
WARN, &fpos(tag), SymName(actual(target_val(cs))));
debug1(DCR, DD, " tag = %s", EchoObject(tag));
}
else if( StringEqual(string(tag), STR_EMPTY) )
{
debug1(DCR, DD, " ignoring cross (own tag) %s (empty tag)",
SymName(sym));
}
else
{
cs_fnum(tag) = file_num(fpos(tag));
cs_type(tag) = CROSS_LIT;
Link(cs, tag);
debug4(DCR, DD, " storing cross (%s) %s&%s %s",
Image(cs_type(tag)), SymName(sym), string(tag), "?");
}
}
else if( type(actual(par)) == RPAR )
{
/* replace any oversized right parameter by question marks */
Child(y, Down(par));
switch( type(y) )
{
case WORD:
case QWORD:
case ACAT:
case OPEN:
case NEXT:
case NULL_CLOS:
case CROSS:
case FORCE_CROSS:
case TAGGED:
/* leave objects of these types as is */
break;
default:
/* replace all other types by three question marks */
tmp = MakeWord(WORD, AsciiToFull("???"), &fpos(y));
ReplaceNode(tmp, y);
DisposeObject(y);
break;
}
}
}
}
/* if new target is already writable, write it */
if( Down(cs) != cs )
{
debug2(DCR, DD, " writing %s cross_targ %s", SymName(sym),
EchoObject(target_val(cs)));
AppendToFile(target_val(cs), target_file(cs), &target_pos(cs),
&target_lnum(cs));
DisposeObject(target_val(cs));
target_val(cs) = nilobj;
for( link = Down(cs); link != cs; link = NextDown(link) )
{ Child(tag, link);
assert( is_word(type(tag)) && !StringEqual(string(tag), STR_EMPTY),
"CrossSeq: non-WORD or empty tag!" );
switch( cs_type(tag) )
{
case CROSS_LIT:
case CROSS_FOLL:
case CROSS_FOLL_OR_PREC:
debug3(DCR, DD, " inserting cross (foll) %s&%s %s", SymName(sym),
string(tag), "0");
DbInsert(NewCrossDb, FALSE, sym, string(tag), &fpos(tag),
STR_ZERO, target_file(cs), (long) target_pos(cs),
target_lnum(cs), TRUE);
link = PrevDown(link);
DisposeChild(NextDown(link));
break;
case GALL_FOLL:
case GALL_PREC:
case GALL_FOLL_OR_PREC:
break;
default:
assert(FALSE, "CrossSequence: cs_type!");
break;
}
}
target_state(cs) = WRITTEN_TARGET;
}
break;
default:
assert1(FALSE, "CrossSequence:", Image(ctype));
break;
} /* end switch */
debug0(DCR, D, "] CrossSequence returning.");
debug0(DCR, D, " cs =");
ifdebug(DCR, DD, DebugObject(cs));
} /* end CrossSequence */
/*@::CrossClose()@************************************************************/
/* */
/* CrossClose() */
/* */
/* Check for dangling forward references, and convert old cross reference */
/* database to new one. */
/* */
/*****************************************************************************/
void CrossClose(void)
{ OBJECT link, cs, ylink, y, sym; BOOLEAN g; int len, count;
FILE_NUM dfnum; long dfpos, cont; int dlnum;
FULL_CHAR buff[MAX_BUFF], seq[MAX_BUFF], tag[MAX_BUFF];
debug0(DCR, D, "[ CrossClose()");
ifdebug(DCR, DD, if( RootCross != nilobj ) DebugObject(RootCross));
/* if suppressing cross referencing, return */
if( !AllowCrossDb )
{ debug0(DCR, DD, "CrossClose returning (!AllowCrossDb).");
return;
}
/* check for dangling forward references and dispose cross ref structures */
if( RootCross != nilobj )
{ for( link = Down(RootCross); link != RootCross; link = NextDown(link) )
{ Child(cs, link);
sym = symb(cs);
assert( type(cs) == CROSS_SYM, "CrossClose: type(cs)!" );
count = 0;
for( ylink = Down(cs); ylink != cs; ylink = NextDown(ylink) )
{ Child(y, ylink);
assert( is_word(type(y)) && !StringEqual(string(y), STR_EMPTY),
"CrossClose: GALL_TARG y!" );
switch( cs_type(y) )
{
case CROSS_FOLL:
debug2(DCR, DD, "cs_type(y) = %s, y = %s",
Image(cs_type(y)), EchoObject(y));
if( count < 5 )
Error(10, 16, "no %s follows this %s%s%s", WARN, &fpos(y),
SymName(sym), SymName(sym), KW_CROSS, KW_FOLLOWING);
else if( count == 5 )
Error(10, 17, "and more undefined %s%s%s", WARN, no_fpos,
SymName(sym), KW_CROSS, KW_FOLLOWING);
count++;
break;
case CROSS_FOLL_OR_PREC:
/* no following target, so switch to preceding */
if( target_state(cs) == NO_TARGET )
{ Error(10, 18, "no %s follows or precedes this %s%s%s", WARN,
&fpos(y), SymName(sym), SymName(sym),KW_CROSS,KW_FOLL_OR_PREC);
break;
}
if( target_state(cs) == SEEN_TARGET )
{
debug2(DCR, DD, " inserting %s cross_targ %s",
SymName(sym), target_val(cs));
AppendToFile(target_val(cs), target_file(cs), &target_pos(cs),
&target_lnum(cs));
DisposeObject(target_val(cs));
target_val(cs) = nilobj;
target_state(cs) = WRITTEN_TARGET;
}
if( !is_word(type(y)) || StringEqual(string(y), STR_EMPTY) )
{
debug2(DCR, DD, " CROSS_FOLL_OR_PREC %s put none for %s",
SymName(sym), EchoObject(y));
y = MakeWord(WORD, STR_NONE, no_fpos);
}
debug4(DCR, DD, " inserting cross (%s) %s&%s %s",
Image(cs_type(y)), SymName(sym), string(y), "0");
DbInsert(NewCrossDb, FALSE, sym, string(y), &fpos(y), STR_ZERO,
target_file(cs), (long) target_pos(cs), target_lnum(cs), TRUE);
break;
case GALL_FOLL:
debug2(DCR, DD, "cs_type(y) = %s, y = %s",
Image(cs_type(y)), EchoObject(y));
if( count < 5 )
Error(10, 19, "no %s follows this %s%s%s", WARN, &fpos(y),
SymName(sym), SymName(sym), KW_CROSS, KW_FOLLOWING);
else if( count == 5 )
Error(10, 20, "and more undefined %s%s%s", WARN, no_fpos,
SymName(sym), KW_CROSS, KW_FOLLOWING);
DbInsert(NewCrossDb, TRUE, sym, STR_NONE, no_fpos,
string(y), cs_fnum(y), (long) cs_pos(y), cs_lnum(y), FALSE);
count++;
break;
case GALL_FOLL_OR_PREC:
if( gall_tag(cs) == nilobj )
{ Error(10, 21, "no %s precedes or follows this %s%s%s", WARN,
&fpos(y), SymName(sym), SymName(sym),KW_CROSS,KW_FOLL_OR_PREC);
gall_tag(cs) = MakeWord(WORD, STR_NONE, no_fpos);
}
debug3(DCR, DD, " inserting galley (foll_or_prec) %s&%s %s",
SymName(sym), string(gall_tag(cs)), string(y));
DbInsert(NewCrossDb, TRUE, sym, string(gall_tag(cs)), no_fpos,
string(y), cs_fnum(y), (long) cs_pos(y), cs_lnum(y), FALSE);
break;
default:
debug1(DCR, DD, "CrossClose: unknown cs_type %s",
Image(cs_type(y)));
assert(FALSE, "CrossClose: unknown cs_type!");
break;
}
}
ifdebug(ANY, D,
if( target_state(cs) == SEEN_TARGET ) DisposeObject(target_val(cs));
if( gall_tag(cs) != nilobj ) DisposeObject(gall_tag(cs));
);
}
ifdebug(ANY, D, DisposeObject(RootCross); );
}
/* add to NewCrossDb those entries of OldCrossDb from other source files */
/* but set check to FALSE so that we don't worry about duplication there */
cont = 0L; len = StringLength(DATA_SUFFIX);
while( DbRetrieveNext(OldCrossDb,&g,&sym,tag,seq,&dfnum,&dfpos,&dlnum,&cont))
{ if( g ) continue;
StringCopy(buff, FileName(dfnum));
StringCopy(&buff[StringLength(buff) - len], STR_EMPTY);
if( FileNum(buff, STR_EMPTY) == NO_FILE )
DbInsert(NewCrossDb, FALSE, sym, tag, no_fpos, seq, dfnum, dfpos,
dlnum, FALSE);
}
/* close OldCrossDb's .li file so that NewCrossDb can use its name */
DbClose(OldCrossDb);
/* make NewCrossDb readable, for next run */
DbConvert(NewCrossDb, TRUE);
debug0(DCR, D, "] CrossClose returning.");
ifdebug(DCR, DD, crtab_debug(crossref_tab, stderr));
} /* end CrossClose */