diff options
author | Jeffrey H. Kingston <jeff@it.usyd.edu.au> | 2010-09-14 19:21:41 +0000 |
---|---|---|
committer | Jeffrey H. Kingston <jeff@it.usyd.edu.au> | 2010-09-14 19:21:41 +0000 |
commit | 71bdb35d52747e6d7d9f55df4524d57c2966be94 (patch) | |
tree | 480ee5eefccc40d5f3331cc52d66f722fd19bfb9 /z33.c | |
parent | b41263ea7578fa9742486135c762803b52794105 (diff) | |
download | lout-71bdb35d52747e6d7d9f55df4524d57c2966be94.tar.gz |
Lout 3.17.
git-svn-id: http://svn.savannah.nongnu.org/svn/lout/trunk@2 9365b830-b601-4143-9ba8-b4a8e2c3339c
Diffstat (limited to 'z33.c')
-rw-r--r-- | z33.c | 861 |
1 files changed, 861 insertions, 0 deletions
@@ -0,0 +1,861 @@ +/*@z33.c:Database Service:OldCrossDb(), NewCrossDb(), SymToNum()@*************/ +/* */ +/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.17) */ +/* COPYRIGHT (C) 1991, 1999 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: z33.c */ +/* MODULE: Database Service */ +/* EXTERNS: OldCrossDb, NewCrossDb, DbCreate(), DbInsert(), */ +/* DbConvert(), DbClose(), DbLoad(), DbRetrieve(), */ +/* DbRetrieveNext() */ +/* */ +/*****************************************************************************/ +#define INIT_DBCHECK_NUM 107 +#include "externs.h" + + +/*****************************************************************************/ +/* */ +/* DBCHECK_TABLE */ +/* */ +/* A symbol table holding all non-galley cross references, basically */ +/* implementing a function (sym, tag) -> fpos (if any). */ +/* */ +/* dtab_new(newsize) New empty table, newsize capacity */ +/* dtab_insert(x, S) Insert new (sym, tag) pair x into S */ +/* dtab_retrieve(sym, tag, S) Retrieve (sym, tag) pair from S */ +/* dtab_debug(S, fp) Debug print of table S to file fp */ +/* */ +/*****************************************************************************/ + +typedef struct +{ int dbchecktab_size; /* size of table */ + int dbchecktab_count; /* number of objects held */ + OBJECT dbchecktab_item[1]; +} *DBCHECK_TABLE; + +#define dtab_size(S) (S)->dbchecktab_size +#define dtab_count(S) (S)->dbchecktab_count +#define dtab_item(S, i) (S)->dbchecktab_item[i] + +#define hash(pos, sym, tag, S) \ +{ FULL_CHAR *p = tag; \ + pos = (long) sym; \ + while( *p ) pos += *p++; \ + pos = pos % dtab_size(S); \ +} + +static DBCHECK_TABLE dtab_new(int newsize) +{ DBCHECK_TABLE S; int i; + ifdebug(DMA, D, DebugRegisterUsage(MEM_DBCHECK, 1, + 2*sizeof(int) + newsize * sizeof(OBJECT))); + S = (DBCHECK_TABLE) + malloc(2*sizeof(int) + newsize * sizeof(OBJECT)); + if( S == (DBCHECK_TABLE) NULL ) + Error(33, 1, "run out of memory enlarging dbcheck table", FATAL, no_fpos); + dtab_size(S) = newsize; + dtab_count(S) = 0; + for( i = 0; i < newsize; i++ ) dtab_item(S, i) = nilobj; + return S; +} /* end dtab_new */ + +static void dtab_insert(OBJECT x, DBCHECK_TABLE *S); + +static DBCHECK_TABLE dtab_rehash(DBCHECK_TABLE S, int newsize) +{ DBCHECK_TABLE NewS; int i; OBJECT link, z; + NewS = dtab_new(newsize); + for( i = 0; i < dtab_size(S); i++ ) + { if( dtab_item(S, i) != nilobj ) + { OBJECT ent = dtab_item(S, i); + assert( type(ent) == ACAT, "dtab_rehash: ACAT!" ); + for( link = Down(ent); link != ent; link = NextDown(link) ) + { Child(z, link); + dtab_insert(z, &NewS); + } + DisposeObject(ent); + } + } + ifdebug(DMA, D, DebugRegisterUsage(MEM_DBCHECK, -1, + -(2*sizeof(int) + dtab_size(S) * sizeof(OBJECT)))); + free(S); + return NewS; +} /* end dtab_rehash */ + +static void dtab_insert(OBJECT x, DBCHECK_TABLE *S) +{ long pos; OBJECT z, link, y; + if( dtab_count(*S) == dtab_size(*S) - 1 ) /* one less since 0 unused */ + *S = dtab_rehash(*S, 2*dtab_size(*S)); + dtab_count(*S)++; + hash(pos, db_checksym(x), string(x), *S); + if( dtab_item(*S, pos) == nilobj ) New(dtab_item(*S, pos), ACAT); + z = dtab_item(*S, pos); + for( link = Down(z); link != z; link = NextDown(link) ) + { Child(y, link); + if( db_checksym(x) == db_checksym(y) && StringEqual(string(x), string(y)) ) + { assert(FALSE, "Dbcheck: entry inserted twice"); + } + } + Link(dtab_item(*S, pos), x); +} /* end dtab_insert */ + +static OBJECT dtab_retrieve(OBJECT sym, FULL_CHAR *tag, DBCHECK_TABLE S) +{ OBJECT x, link, y; long pos; + hash(pos, sym, tag, S); + x = dtab_item(S, pos); + if( x == nilobj ) return nilobj; + for( link = Down(x); link != x; link = NextDown(link) ) + { Child(y, link); + if( sym == db_checksym(y) && StringEqual(tag, string(y)) ) + return y; + } + return nilobj; +} /* end dtab_retrieve */ + +#if DEBUG_ON +static void dtab_debug(DBCHECK_TABLE S, FILE *fp) +{ int i; OBJECT x, link, y; + fprintf(fp, " table size: %d; current number of items: %d\n", + dtab_size(S), dtab_count(S)); + for( i = 0; i < dtab_size(S); i++ ) + { x = dtab_item(S, i); + fprintf(fp, "dtab_item(S, %d) =", i); + if( x == nilobj ) + fprintf(fp, " <nilobj>"); + else if( type(x) != ACAT ) + fprintf(fp, " not ACAT!"); + else for( link = Down(x); link != x; link = NextDown(link) ) + { Child(y, link); + fprintf(fp, " %s&&%s", + is_word(type(y)) ? SymName(db_checksym(y)) : AsciiToFull("?"), + is_word(type(y)) ? string(y) : AsciiToFull("not-WORD!")); + } + fprintf(fp, "\n"); + } +} /* end dtab_debug */ +#endif + +static DBCHECK_TABLE DbCheckTable; /* the dbcheck table */ +static BOOLEAN DbCheckTableInit = FALSE; /* TRUE if table inited */ + + +/*****************************************************************************/ +/* */ +/* OldCrossDb Database containing cross references from previous run. */ +/* NewCrossDb Writable database of cross references from this run. */ +/* */ +/*****************************************************************************/ + +OBJECT OldCrossDb, NewCrossDb; + + +/*****************************************************************************/ +/* */ +/* #define SymToNum(db, sym, num, gall) */ +/* */ +/* Set num to the number used to refer to sym in database db. If sym is */ +/* not currently referred to in db, create a new number and record sym. */ +/* If gall is true, sym is the target of galleys stored in this database. */ +/* Store in boolean fields db_targ(link) and is_extern_target(sym). */ +/* */ +/*****************************************************************************/ + +#define SymToNum(db, sym, num, gall) \ +{ OBJECT link, yy; int count; \ + count = 0; \ + for( link = Down(db); link != db; link = NextDown(link) ) \ + { Child(yy, link); \ + assert(type(yy)==CROSS_SYM || type(yy)==ACAT, "SymToNum: yy!"); \ + if( type(yy) != CROSS_SYM ) continue; \ + if( symb(yy) == sym ) break; \ + if( number(link) > count ) count = number(link); \ + } \ + if( link == db ) \ + { if( cross_sym(sym) == nilobj ) CrossInit(sym); \ + Link(db, cross_sym(sym)); \ + link = LastDown(db); \ + number(link) = count + 1; \ + db_targ(link) = FALSE; \ + } \ + num = number(link); \ + if( gall ) db_targ(link) = is_extern_target(sym) = \ + uses_extern_target(sym) = TRUE; \ +} /* end SymToNum */ + + +/*@::NumToSym(), DbCreate()@**************************************************/ +/* */ +/* #define NumToSym(db, num, sym) */ +/* */ +/* Set sym to the symbol which is referred to in database db by num. */ +/* */ +/*****************************************************************************/ + +#define NumToSym(db, num, sym) \ +{ OBJECT link, y; \ + for( link = Down(db); link != db; link = NextDown(link) ) \ + { Child(y, link); \ + if( type(y) == CROSS_SYM && number(link) == num ) break; \ + } \ + assert( link != db, "NumToSym: no sym"); \ + assert( type(y) == CROSS_SYM, "NumToSym: y!" ); \ + sym = symb(y); \ +} /* end NumToSym */ + + +/*****************************************************************************/ +/* */ +/* OBJECT DbCreate(x) */ +/* */ +/* Create a new writable database with name (i.e. file stem) x and file */ +/* position fpos for error messages. */ +/* */ +/*****************************************************************************/ + +OBJECT DbCreate(OBJECT x) +{ OBJECT db = x; + debug1(DBS, DD, "DbCreate(%s)", string(db)); + assert( is_word(type(x)), "DbCreate: !is_word(type(x))" ); + reading(db) = FALSE; db_filep(db) = null; + debug1(DBS, DD, "DbCreate returning %s", EchoObject(db)); + return db; +} /* end DbCreate */ + + +/*@::DbInsert()@**************************************************************/ +/* */ +/* DbInsert(db, gall, sym, tag, tagfpos, seq, dfnum, dlnum, dfpos) */ +/* */ +/* Insert a new entry into writable database db. The primary key of the */ +/* entry has these three parts: */ +/* */ +/* gall TRUE if inserting a galley */ +/* sym The symbol which is the target of this entry */ +/* tag The tag of this target (must be a non-null string) */ +/* */ +/* tagfpos is the file position that the tag originated from. */ +/* There is also an auxiliary key, seq, which enforces an ordering on */ +/* entries with equal primary keys but is not itself ever retrieved. This */ +/* ordering is used for sorted galleys. The value of the entry has the */ +/* following parts: */ +/* */ +/* dfnum The file containing the object */ +/* dfpos The position of the object in that file */ +/* dlnum The line number of the object in the file */ +/* */ +/* If check is TRUE, we need to check whether an entry with this key has */ +/* been inserted before. This will never be the case with galley entries. */ +/* */ +/*****************************************************************************/ + +void DbInsert(OBJECT db, BOOLEAN gall, OBJECT sym, FULL_CHAR *tag, +FILE_POS *tagfpos, FULL_CHAR *seq, FILE_NUM dfnum, long dfpos, int dlnum, +BOOLEAN check) +{ int symnum; OBJECT chk; + static int extra_seq = 0; + FULL_CHAR buff[MAX_BUFF]; + assert( is_word(type(db)), "DbInsert: db!" ); + assert( tag[0] != '\0', "DbInsert: null tag!" ); + assert( seq[0] != '\0', "DbInsert: null seq!" ); + ifdebug(DPP, D, ProfileOn("DbInsert")); + debug6(DBS, D, "DbInsert(%s, %s, %s, %s, %s, %s, dlnum, dfpos)", + string(db), bool(gall), SymName(sym), tag, seq, + dfnum == NO_FILE ? AsciiToFull(".") : FileName(dfnum)); + assert(!reading(db), "DbInsert: insert into reading database"); + + /* if required, check that (sym, tag) not already inserted */ + if( check ) + { + debug2(DBS, DD, " checking %s&&%s, DbCheckTable =", SymName(sym), tag); + if( !DbCheckTableInit ) + { DbCheckTable = dtab_new(INIT_DBCHECK_NUM); + DbCheckTableInit = TRUE; + } + ifdebug(DBS, DD, dtab_debug(DbCheckTable, stderr)); + chk = dtab_retrieve(sym, tag, DbCheckTable); + if( chk == nilobj ) + { chk = MakeWord(WORD, tag, tagfpos); + db_checksym(chk) = sym; + dtab_insert(chk, &DbCheckTable); + } + else + { if( file_num(fpos(chk)) > 0 ) + Error(33, 4, "cross reference %s&&%s used previously, at%s", + WARN, tagfpos, SymName(sym), tag, EchoFilePos(&fpos(chk))); + else Error(33, 5, "cross reference %s&&%s used previously", + WARN, tagfpos, SymName(sym), tag); + } + } + + /* open database index file if not already done */ + if( db_filep(db) == null ) + { if( StringLength(string(db)) + StringLength(NEW_INDEX_SUFFIX) >= MAX_BUFF ) + Error(33, 2, "database file name %s%s is too long", + FATAL, no_fpos, string(db), NEW_INDEX_SUFFIX); + StringCopy(buff, string(db)); + StringCat(buff, NEW_INDEX_SUFFIX); + db_filep(db) = StringFOpen(buff, WRITE_BINARY); + if( db_filep(db) == null ) + Error(33, 3, "cannot write to database file %s", FATAL, &fpos(db), buff); + } + + /* work out database index file entry and append it to file */ + if( dfnum != NO_FILE ) + { StringCopy(buff, FileName(dfnum)); + StringCopy(&buff[StringLength(buff)-StringLength(DATA_SUFFIX)], STR_EMPTY); + } + else StringCopy(buff, AsciiToFull(".")); + SymToNum(db, sym, symnum, gall); + ifdebug(DBS, DD, + fprintf(stderr, " -> %s%d&%s\t%s\t%ld\t%d\t%s\n", gall ? "0" : "", symnum, + tag, seq, dfpos, dlnum, buff); + ); + fprintf(db_filep(db), "%s%d&%s\t%s\t%s\t%ld\t%d\t%s\n", gall ? "0" : "", + symnum, tag, seq, StringFiveInt(++extra_seq), dfpos, dlnum, buff); + + /* return */ + debug0(DBS, DD, "DbInsert returning."); + ifdebug(DPP, D, ProfileOff("DbInsert")); +} /* end DbInsert */ + + +/*@::DbConvert(), DbClose()@**************************************************/ +/* */ +/* DbConvert(db, full_name) */ +/* */ +/* Convert database db from writable to readable, then dispose it. */ +/* full_name is TRUE if symbols are to be known by their full path name. */ +/* */ +/*****************************************************************************/ + +void DbConvert(OBJECT db, BOOLEAN full_name) +{ FULL_CHAR oldname[MAX_BUFF+10], newname[MAX_BUFF]; + OBJECT link, y; + ifdebug(DPP, D, ProfileOn("DbConvert")); + debug2(DBS, DD, "DbConvert( %ld %s )", (long) db, string(db)); + assert( !reading(db), "DbConvert: reading database"); + StringCopy(newname, string(db)); + StringCat(newname, INDEX_SUFFIX); + StringCopy(oldname, string(db)); + StringCat(oldname, NEW_INDEX_SUFFIX); + if( db_filep(db) != null ) + { + fprintf(db_filep(db), "00 %s %s\n", LOUT_VERSION, "database index file"); + for( link = Down(db); link != db; link = NextDown(link) ) + { Child(y, link); + assert( type(y) == CROSS_SYM || type(y) == ACAT, "DbConvert: y!" ); + if( type(y) != CROSS_SYM ) continue; + fprintf(db_filep(db), "%s %d %s\n", + db_targ(link) ? "00target" : "00symbol", + number(link), + full_name ? FullSymName(symb(y), AsciiToFull(" ")) : SymName(symb(y))); + } + fclose(db_filep(db)); + debug2(DBS, DD, " calling SortFile(%s, %s)", oldname, newname); + SortFile(oldname, newname); + } + else StringRemove(newname); + StringRemove(oldname); + DeleteNode(db); + debug0(DBS, DD, "DbConvert returning."); + ifdebug(DPP, D, ProfileOff("DbConvert")); +} /* end DbConvert */ + + +/*****************************************************************************/ +/* */ +/* DbClose(db) */ +/* */ +/* Close readable database db. */ +/* */ +/*****************************************************************************/ + +void DbClose(OBJECT db) +{ if( db != nilobj && !in_memory(db) && db_filep(db) != NULL ) + { fclose(db_filep(db)); + db_filep(db) = NULL; + } +} /* end DbClose */ + + +/*@::DbLoad()@****************************************************************/ +/* */ +/* OBJECT DbLoad(stem, fpath, create, symbs, in_mem) */ +/* */ +/* Open for reading the database whose index file name is string(stem).li. */ +/* This file has not yet been defined; its search path is fpath. If it */ +/* will not open and create is true, try creating it from string(stem).ld. */ +/* */ +/* symbs is an ACAT of CLOSUREs showing the symbols that the database may */ +/* contain; or nilobj if the database may contain any symbol. */ +/* */ +/* If in_mem is true, this database index is to be kept in internal memory, */ +/* rather than an external file, as a speed optimization. */ +/* */ +/*****************************************************************************/ + +OBJECT DbLoad(OBJECT stem, int fpath, BOOLEAN create, OBJECT symbs, + BOOLEAN in_mem) +{ FILE *fp; OBJECT db, t, res, tag, par, sym, link, y; + int i, lnum, dlnum, num, count, leftp; + FILE_NUM index_fnum, dfnum; long dfpos; + BOOLEAN gall; FULL_CHAR line[MAX_BUFF], sym_name[MAX_BUFF]; char *gotline; + ifdebug(DPP, D, ProfileOn("DbLoad")); + debug3(DBS, DD, "[ DbLoad(%s, %d, %s, -)", string(stem), fpath, bool(create)); + + /* open or else create index file fp */ + debug0(DFS, D, " calling DefineFile from DbLoad (1)"); + index_fnum = DefineFile(string(stem), INDEX_SUFFIX, &fpos(stem), INDEX_FILE, + fpath); + fp = OpenFile(index_fnum, create, FALSE); + + /* read first line of database index file, which should have the version */ + if( fp != null ) + { if( StringFGets(line, MAX_BUFF, fp) == NULL || + !StringBeginsWith(&line[3], LOUT_VERSION) ) + { + /* out of date, pretend it isn't there at all */ + StringRemove(FileName(index_fnum)); + fp = null; + } + } + + if( fp == null && create ) + { db = nilobj; + debug0(DFS, D, " calling DefineFile from DbLoad (2)"); + dfnum = DefineFile(string(stem), DATA_SUFFIX, &fpos(stem), + DATABASE_FILE, DATABASE_PATH); + dfpos = 0L; LexPush(dfnum, 0, DATABASE_FILE, 1, FALSE); + t = LexGetToken(); + dlnum = line_num(fpos(t)); + while( type(t) == LBR ) + { res = Parse(&t, StartSym, FALSE, FALSE); + if( t != nilobj || type(res) != CLOSURE ) + Error(33, 6, "syntax error in database file %s", + FATAL, &fpos(res), FileName(dfnum)); + assert( symbs != nilobj, "DbLoad: create && symbs == nilobj!" ); + if( symbs != nilobj ) + { for( link = Down(symbs); link != symbs; link = NextDown(link) ) + { Child(y, link); + if( type(y) == CLOSURE && actual(y) == actual(res) ) break; + } + if( link == symbs ) + Error(33, 7, "%s found in database but not declared in %s line", + FATAL, &fpos(res), SymName(actual(res)), KW_DATABASE); + } + for( tag = nilobj, link = Down(res); link != res; link = NextDown(link) ) + { Child(par, link); + if( type(par) == PAR && is_tag(actual(par)) && Down(par) != par ) + { Child(tag, Down(par)); + break; + } + } + if( tag == nilobj ) + Error(33, 8, "database symbol %s has no tag", + FATAL, &fpos(res), SymName(actual(res))); + tag = ReplaceWithTidy(tag, TRUE); /* && */ + if( !is_word(type(tag)) ) + Error(33, 9, "database symbol tag is not a simple word", + FATAL, &fpos(res)); + if( StringEqual(string(tag), STR_EMPTY) ) + Error(33, 10, "database symbol tag is an empty word", FATAL,&fpos(res)); + if( db == nilobj ) + { StringCopy(line, FileName(dfnum)); + i = StringLength(line) - StringLength(INDEX_SUFFIX); + assert( i > 0, "DbLoad: FileName(dfnum) (1)!" ); + StringCopy(&line[i], STR_EMPTY); + db = DbCreate(MakeWord(WORD, line, &fpos(stem))); + } + DbInsert(db, FALSE, actual(res), string(tag), &fpos(tag), STR_ZERO, + NO_FILE, dfpos, dlnum, TRUE); + DisposeObject(res); dfpos = LexNextTokenPos(); t = LexGetToken(); + dlnum = line_num(fpos(t)); + } + if( type(t) != END ) + Error(33, 11, "%s or end of file expected here", FATAL, &fpos(t), KW_LBR); + LexPop(); + if( db == nilobj ) + { StringCopy(line, FileName(dfnum)); + i = StringLength(line) - StringLength(INDEX_SUFFIX); + assert( i > 0, "DbLoad: FileName(dfnum) (2)!" ); + StringCopy(&line[i], STR_EMPTY); + db = DbCreate(MakeWord(WORD, line, &fpos(stem))); + } + DbConvert(db, FALSE); + if( (fp = OpenFile(index_fnum, FALSE, FALSE)) == null || + StringFGets(line, MAX_BUFF, fp) == NULL || + !StringBeginsWith(&line[3], LOUT_VERSION) ) + Error(33, 12, "cannot open database file %s", + FATAL, &fpos(db), FileName(index_fnum)); + } + + /* set up database record */ + StringCopy(line, FileName(index_fnum)); + i = StringLength(line) - StringLength(INDEX_SUFFIX); + assert( i > 0, "DbLoad: FileName(index_fnum)!" ); + StringCopy(&line[i], STR_EMPTY); + db = MakeWord(WORD, line, &fpos(stem)); + reading(db) = TRUE; + in_memory(db) = in_mem; + if( symbs != nilobj ) + { assert( type(symbs) == ACAT, "DbLoad: type(symbs)!" ); + Link(db, symbs); + } + if( fp == null ) + { debug1(DBS, DD, "] DbLoad returning (empty) %s", string(db)); + db_filep(db) = null; + db_lines(db) = (LINE *) NULL; + ifdebug(DPP, D, ProfileOff("DbLoad")); + return db; + } + + /* read header lines of index file, find its symbols */ + leftp = 0; lnum = 1; + gotline = StringFGets(line, MAX_BUFF, fp); + while( gotline != NULL ) + { + if( line[0] != '0' || line[1] != '0' ) break; + lnum++; + leftp = (int) ftell(fp); + gall = StringBeginsWith(line, AsciiToFull("00target ")); + sscanf( (char *) line, gall ? "00target %d" : "00symbol %d", &num); + for( i = 9; line[i] != CH_SPACE && line[i] != '\0'; i++ ); + if( symbs == nilobj ) + { + /* any symbols are possible, full path names in index file required */ + count = 0; sym = StartSym; + while( line[i] != CH_NEWLINE && line[i] != '\0' ) + { PushScope(sym, FALSE, FALSE); count++; + sscanf( (char *) &line[i+1], "%s", sym_name); + sym = SearchSym(sym_name, StringLength(sym_name)); + i += StringLength(sym_name) + 1; + } + for( i = 1; i <= count; i++ ) PopScope(); + } + else + { + /* only symbs symbols possible, full path names not required */ + sym = nilobj; + sscanf( (char *) &line[i+1], "%s", sym_name); + for( link = Down(symbs); link != symbs; link = NextDown(link) ) + { Child(y, link); + assert( type(y) == CLOSURE, "DbLoad: type(y) != CLOSURE!" ); + if( StringEqual(sym_name, SymName(actual(y))) ) + { sym = actual(y); + break; + } + } + } + if( sym != nilobj && sym != StartSym ) + { if( cross_sym(sym) == nilobj ) CrossInit(sym); + Link(db, cross_sym(sym)); + link = LastDown(db); + number(link) = num; db_targ(link) = gall; + if( gall ) is_extern_target(sym) = uses_extern_target(sym) = TRUE; + } + else + { Error(33, 13, "undefined symbol in database file %s (line %d)", + WARN, &fpos(db), FileName(index_fnum), lnum); + debug1(DBS, DD, "] DbLoad returning %s (error)", string(db)); + fclose(fp); + in_memory(db) = FALSE; + db_filep(db) = null; /* subsequently treated like an empty database */ + ifdebug(DPP, D, ProfileOff("DbLoad")); + return db; + } + gotline = StringFGets(line, MAX_BUFF, fp); + } + + /* if in_memory, go on to read the entire database index into memory */ + if( in_memory(db) ) + { int len; + if( gotline == NULL ) + db_lines(db) = 0; + else + { + db_lines(db) = ReadLines(fp, FileName(index_fnum), line, &len); + db_lineslen(db) = len; + SortLines(db_lines(db), db_lineslen(db)); + } + } + else /* external, save leftpos and file pointer */ + { db_filep(db) = fp; + left_pos(db) = leftp; + } + + /* return */ + debug1(DBS, DD, "] DbLoad returning %s", string(db)); + ifdebug(DPP, D, ProfileOff("DbLoad")); + return db; +} /* end DbLoad */ + + +/*@::SearchFile()@************************************************************/ +/* */ +/* static BOOLEAN SearchFile(fp, left, right, str, line) */ +/* */ +/* File fp is a text file. left is the beginning of a line, right is the */ +/* end of a line. Search the file by binary search for a line beginning */ +/* with str. If found, return it in line, else return FALSE. */ +/* */ +/*****************************************************************************/ + +static BOOLEAN SearchFile(FILE *fp, int left, int right, +FULL_CHAR *str, FULL_CHAR *line) +{ int l, r, mid, mid_end; FULL_CHAR buff[MAX_BUFF]; BOOLEAN res; + ifdebug(DPP, D, ProfileOn("SearchFile")); + debug3(DBS, DD, "SearchFile(fp, %d, %d, %s, line)", left, right, str); + + l = left; r = right; + while( l <= r ) + { + /* loop invt: (l==0 or fp[l-1]==CH_NEWLINE) and (fp[r] == CH_NEWLINE) */ + /* and first key >= str lies in the range fp[l..r+1] */ + + /* find line near middle of the range; mid..mid_end brackets it */ + debug2(DBS, DD, " start loop: l = %d, r = %d", l, r); + mid = (l + r)/2; + fseek(fp, (long) mid, SEEK_SET); + do { mid++; } while( getc(fp) != CH_NEWLINE ); + if( mid == r + 1 ) + { mid = l; + fseek(fp, (long) mid, SEEK_SET); + } + StringFGets(line, MAX_BUFF, fp); + mid_end = (int) ftell(fp) - 1; + debug3(DBS, DD, " mid: %d, mid_end: %d, line: %s", mid, mid_end, line); + assert( l <= mid, "SearchFile: l > mid!" ); + assert( mid < mid_end, "SearchFile: mid >= mid_end!" ); + assert( mid_end <= r, "SearchFile: mid_end > r!" ); + + /* compare str with this line and prepare next step */ + debug2(DBS, DD, " comparing key %s with line %s", str, line); + if( StringLessEqual(str, line) ) r = mid - 1; + else l = mid_end + 1; + } /* end while */ + + /* now first key >= str lies in fp[l]; compare it with str */ + if( l < right ) + { fseek(fp, (long) l, SEEK_SET); + StringFGets(line, MAX_BUFF, fp); + sscanf( (char *) line, "%[^\t]", buff); + res = StringEqual(str, buff); + } + else res = FALSE; + debug1(DBS, DD, "SearchFile returning %s", bool(res)); + ifdebug(DPP, D, ProfileOff("SearchFile")); + return res; +} /* end SearchFile */ + + +/*@::SearchLines()@***********************************************************/ +/* */ +/* static BOOLEAN SearchLines(LINE *lines, int left, int right, str, lnum) */ +/* */ +/* Search the sorted array of LINE arrays lines[left..right] for a line */ +/* beginning with str, and return TRUE if found else FALSE. */ +/* */ +/* If TRUE is returned then the number of the line is in *lnum. */ +/* */ +/*****************************************************************************/ + +static BOOLEAN SearchLines(LINE *lines, int left, int right, FULL_CHAR *str, + int *lnum) +{ int l, r, mid; FULL_CHAR buff[MAX_BUFF]; + BOOLEAN res; + debug3(DBS, D, "SearchLines(lines, %d, %d, %s, lnum)", left, right, str); + if( right < left ) + { + debug0(DBS, D, "SearchLines returning FALSE (empty lines)"); + return FALSE; + } + l = left; + r = right - 1; + while( l <= r ) + { + /* loop invt: first key >= str (if any) lies in the range lines[l..r+1] */ + /* and left <= l <= right and r < right */ + mid = (l + r) / 2; + debug4(DBS, D, " [l %d, r %d] examining lines[%d] = %s", l, r, mid, + lines[mid]); + if( StringLessEqual(str, (FULL_CHAR *) lines[mid]) ) r = mid - 1; + else l = mid + 1; + } + sscanf( (char *) lines[l], "%[^\t]", buff); + if( StringEqual(str, buff) ) + { + res = TRUE; + *lnum = l; + debug1(DBS, D, "SearchLines returning TRUE (lnum %d)", *lnum); + } + else + { res = FALSE; + debug0(DBS, D, "SearchLines returning FALSE"); + } + return res; +} /* end SearchLines */ + + +/*@::DbRetrieve()@************************************************************/ +/* */ +/* BOOLEAN DbRetrieve(db, gall, sym, tag, seq, dfnum, dfpos, dlnum, cont) */ +/* */ +/* Retrieve the first entry of database db with the given gall, sym and */ +/* tag. Set *seq, *dfnum, *dlnum, *dfpos to the associated value. */ +/* Set *cont to a private value for passing to DbRetrieveNext. */ +/* */ +/*****************************************************************************/ + +BOOLEAN DbRetrieve(OBJECT db, BOOLEAN gall, OBJECT sym, FULL_CHAR *tag, + FULL_CHAR *seq, FILE_NUM *dfnum, long *dfpos, int *dlnum, long *cont) +{ int symnum, lnum; FULL_CHAR line[MAX_BUFF], buff[MAX_BUFF]; + ifdebug(DPP, D, ProfileOn("DbRetrieve")); + debug4(DBS, DD, "DbRetrieve(%s, %s%s&%s)", string(db), gall ? "0" : "", + SymName(sym), tag); + + /* check OK to proceed */ + if( !reading(db) || db_filep(db) == null ) + { debug0(DBS, DD, "DbRetrieve returning FALSE (empty or not reading)"); + ifdebug(DPP, D, ProfileOff("DbRetrieve")); + return FALSE; + } + + /* convert parameters into search key */ + SymToNum(db, sym, symnum, FALSE); + sprintf( (char *) buff, "%s%d&%s", gall ? "0" : "", symnum, tag); + + if( in_memory(db) ) + { + /* search internal table, return if not found; set *cont to continuation */ + if( !SearchLines(db_lines(db), 0, db_lineslen(db) - 1, buff, &lnum) ) + { debug0(DBS, DD, "DbRetrieve returning FALSE (key not present)"); + ifdebug(DPP, D, ProfileOff("DbRetrieve")); + return FALSE; + } + sscanf( (char *) db_lines(db)[lnum], + "%*[^\t]\t%[^\t]\t%*[^\t]\t%ld\t%d\t%[^\n]", seq, dfpos, dlnum, buff); + *cont = lnum+1; + } + else + { + /* search for key in file, return if not found; set *cont to continuatn */ + fseek(db_filep(db), 0L, SEEK_END); + if( !SearchFile(db_filep(db), (int) left_pos(db), + (int) ftell(db_filep(db)) - 1, buff, line) ) + { debug0(DBS, DD, "DbRetrieve returning FALSE (key not present)"); + ifdebug(DPP, D, ProfileOff("DbRetrieve")); + return FALSE; + } + sscanf( (char *) line, + "%*[^\t]\t%[^\t]\t%*[^\t]\t%ld\t%d\t%[^\n]", seq, dfpos, dlnum, buff); + *cont = ftell(db_filep(db)); + } + + /* work out file name if . abbreviation used, and possibly define file */ + if( StringEqual(buff, AsciiToFull(".")) ) + { StringCopy(buff, string(db)); + } + *dfnum = FileNum(buff, DATA_SUFFIX); + if( *dfnum == NO_FILE ) /* can only occur in cross reference database */ + { debug0(DFS, D, " calling DefineFile from DbRetrieve"); + *dfnum = DefineFile(buff, DATA_SUFFIX, &fpos(db), + DATABASE_FILE, SOURCE_PATH); + } + + /* return */ + debug3(DBS, DD, "DbRetrieve returning TRUE (in %s at %ld, line %d)", + FileName(*dfnum), *dfpos, *dlnum); + ifdebug(DPP, D, ProfileOff("DbRetrieve")); + return TRUE; +} /* end DbRetrieve */ + + +/*@::DbRetrieveNext()@********************************************************/ +/* */ +/* BOOLEAN DbRetrieveNext(db, gall, sym, tag, seq, dfnum, dfpos,dlnum,cont) */ +/* */ +/* Retrieve the entry of database db pointed to by *cont. */ +/* Set *gall, *sym, *tag, *seq, *dfnum, *dlnum, *dfpos to the value. */ +/* Reset *cont to the next entry for passing to the next DbRetrieveNext. */ +/* */ +/*****************************************************************************/ + +BOOLEAN DbRetrieveNext(OBJECT db, BOOLEAN *gall, OBJECT *sym, FULL_CHAR *tag, + FULL_CHAR *seq, FILE_NUM *dfnum, long *dfpos, int *dlnum, long *cont) +{ FULL_CHAR line[MAX_BUFF], *cline, fname[MAX_BUFF]; int symnum; + ifdebug(DPP, D, ProfileOn("DbRetrieveNext")); + debug2(DBS, DD, "DbRetrieveNext( %s, %ld )", string(db), *cont); + assert(reading(db), "DbRetrieveNext: not reading"); + + /* check OK to proceed */ + if( db_filep(db) == null ) + { debug0(DBS, DD, "DbRetrieveNext returning FALSE (empty database)"); + ifdebug(DPP, D, ProfileOff("DbRetrieveNext")); + return FALSE; + } + + if( in_memory(db) ) + { + /* get next entry from internal database */ + if( *cont >= db_lineslen(db) ) + { debug0(DBS, DD, "DbRetrieveNext returning FALSE (no successor)"); + ifdebug(DPP, D, ProfileOff("DbRetrieveNext")); + return FALSE; + } + cline = (FULL_CHAR *) db_lines(db)[*cont]; + *gall = (cline[0] == '0' ? 1 : 0); + sscanf((char *)&cline[*gall], "%d&%[^\t]\t%[^\t]\t%*[^\t]\t%ld\t%d\t%[^\n]", + &symnum, tag, seq, dfpos, dlnum, fname); + *cont = *cont + 1; + } + else + { + /* use *cont to find position of next entry; advance *cont */ + fseek(db_filep(db), *cont == 0L ? (long) left_pos(db) : *cont, SEEK_SET); + if( StringFGets(line, MAX_BUFF, db_filep(db)) == NULL ) + { debug0(DBS, DD, "DbRetrieveNext returning FALSE (no successor)"); + ifdebug(DPP, D, ProfileOff("DbRetrieveNext")); + return FALSE; + } + *gall = (line[0] == '0' ? 1 : 0); + sscanf((char *)&line[*gall], "%d&%[^\t]\t%[^\t]\t%*[^\t]\t%ld\t%d\t%[^\n]", + &symnum, tag, seq, dfpos, dlnum, fname); + *cont = ftell(db_filep(db)); + } + + /* work out file name if . abbreviation used, and possibly define file */ + if( StringEqual(fname, AsciiToFull(".")) ) + { StringCopy(fname, string(db)); + } + *dfnum = FileNum(fname, DATA_SUFFIX); + if( *dfnum == NO_FILE ) /* can only occur in cross reference database */ + { debug0(DFS, D, " calling DefineFile from DbRetrieveNext"); + *dfnum = DefineFile(fname, DATA_SUFFIX, &fpos(db), + DATABASE_FILE, SOURCE_PATH); + } + NumToSym(db, symnum, *sym); + + /* return */ + debug3(DBS, DD, "DbRetrieveNext returning TRUE (in %s at %ld, line %d)", + FileName(*dfnum), *dfpos, *dlnum); + ifdebug(DPP, D, ProfileOff("DbRetrieveNext")); + return TRUE; +} /* end DbRetrieveNext */ |