home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / APPS / lout2.lzh / LOUT2 / z33.c < prev    next >
Text File  |  1994-02-25  |  26KB  |  549 lines

  1. /*@z33.c:Database Service:OldCrossDb(), NewCrossDb(), SymToNum()@*************/
  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:         z33.c                                                      */
  26. /*  MODULE:       Database Service                                           */
  27. /*  EXTERNS:      OldCrossDb, NewCrossDb, DbCreate(), DbInsert(),            */
  28. /*                DbConvert(), DbClose(), DbLoad(), DbRetrieve(),            */
  29. /*                DbRetrieveNext()                                           */
  30. /*                                                                           */
  31. /*****************************************************************************/
  32. #include "externs"
  33.  
  34.  
  35. /*****************************************************************************/
  36. /*                                                                           */
  37. /*  OldCrossDb     Database containing cross references from previous run.   */
  38. /*  NewCrossDb     Writable database of cross references from this run.      */
  39. /*                                                                           */
  40. /*****************************************************************************/
  41.  
  42. OBJECT OldCrossDb, NewCrossDb;
  43.  
  44.  
  45. /*****************************************************************************/
  46. /*                                                                           */
  47. /*  #define SymToNum(db, sym, num, gall)                                     */
  48. /*                                                                           */
  49. /*  Set num to the number used to refer to sym in database db.  If sym is    */
  50. /*  not currently referred to in db, create a new number and record sym.     */
  51. /*  If gall is true, sym is the target of galleys stored in this database.   */
  52. /*  Store in boolean fields db_targ(link) and is_extern_target(sym).         */
  53. /*                                                                           */
  54. /*****************************************************************************/
  55.  
  56. #define SymToNum(db, sym, num, gall)                    \
  57. { OBJECT link, yy;  int count;                        \
  58.   count = 0;                                \
  59.   for( link = Down(db);  link != db;  link = NextDown(link) )        \
  60.   { Child(yy, link);                            \
  61.     assert(type(yy)==CROSS_SYM || type(yy)==ACAT, "SymToNum: yy!");    \
  62.     if( type(yy) != CROSS_SYM )  continue;                \
  63.     if( symb(yy) == sym )  break;                    \
  64.     if( number(link) > count )  count = number(link);            \
  65.   }                                    \
  66.   if( link == db )                            \
  67.   { if( cross_sym(sym) == nil )  CrossInit(sym);            \
  68.     link = Link(db, cross_sym(sym));                    \
  69.     number(link) = count + 1;                        \
  70.     db_targ(link) = FALSE;                        \
  71.   }                                    \
  72.   num = number(link);                            \
  73.   if( gall )  db_targ(link) = is_extern_target(sym) =            \
  74.                 uses_extern_target(sym) = TRUE;        \
  75. } /* end SymToNum */
  76.  
  77.  
  78. /*@::NumToSym(), DbCreate()@**************************************************/
  79. /*                                                                           */
  80. /*  #define NumToSym(db, num, sym)                                           */
  81. /*                                                                           */
  82. /*  Set sym to the symbol which is referred to in database db by num.        */
  83. /*                                                                           */
  84. /*****************************************************************************/
  85.  
  86. #define NumToSym(db, num, sym)                        \
  87. { OBJECT link, y;                            \
  88.   for( link = Down(db);  link != db;  link = NextDown(link) )        \
  89.   { Child(y, link);                            \
  90.     if( type(y) == CROSS_SYM && number(link) == num )  break;        \
  91.   }                                    \
  92.   if( link == db )  Error(INTERN, &fpos(db), "NumToSym: no sym!");    \
  93.   assert( type(y) == CROSS_SYM, "NumToSym: y!" );            \
  94.   sym = symb(y);                            \
  95. } /* end NumToSym */
  96.  
  97.  
  98. /*****************************************************************************/
  99. /*                                                                           */
  100. /*  OBJECT DbCreate(x)                                                       */
  101. /*                                                                           */
  102. /*  Create a new writable database with name (i.e. file stem) x and file     */
  103. /*  position fpos for error messages.                                        */
  104. /*                                                                           */
  105. /*****************************************************************************/
  106.  
  107. OBJECT DbCreate(x)
  108. OBJECT x;
  109. { OBJECT db = x;
  110.   debug1(DBS, D, "DbCreate(%s)", string(db));
  111.   assert( is_word(type(x)), "DbCreate: !is_word(type(x))" );
  112.   reading(db) = FALSE;  filep(db) = null;
  113.   debug1(DBS, D, "DbCreate returning %s", EchoObject(db));
  114.   return db;
  115. } /* end DbCreate */
  116.  
  117.  
  118. /*@::DbInsert()@**************************************************************/
  119. /*                                                                           */
  120. /*  DbInsert(db, gall, sym, tag, seq, dfnum, dfpos)                          */
  121. /*                                                                           */
  122. /*  Insert a new entry into writable database db.  The primary key of the    */
  123. /*  entry has these three parts:                                             */
  124. /*                                                                           */
  125. /*      gall        TRUE if inserting a galley                               */
  126. /*      sym         The symbol which is the target of this entry             */
  127. /*      tag         The tag of this target (must be a non-null string)       */
  128. /*                                                                           */
  129. /*  There is also an auxiliary key, seq, which enforces an ordering on       */
  130. /*  entries with equal primary keys but is not itself ever retrieved.  This  */
  131. /*  ordering is used for sorted galleys.  The value of the entry has the     */
  132. /*  following parts:                                                         */
  133. /*                                                                           */
  134. /*      dfnum       The file containing the object                           */
  135. /*      dfpos       The position of the object in that file                  */
  136. /*                                                                           */
  137. /*****************************************************************************/
  138.  
  139. DbInsert(db, gall, sym, tag, seq, dfnum, dfpos)
  140. OBJECT db;  BOOLEAN gall;  OBJECT sym;  FULL_CHAR *tag, *seq;
  141. FILE_NUM dfnum;  long dfpos;
  142. { int symnum;
  143.   FULL_CHAR buff[MAX_LINE];
  144.   assert( is_word(type(db)), "DbInsert: db!" );
  145.   assert( tag[0] != '\0', "DbInsert: null tag!" );
  146.   assert( seq[0] != '\0', "DbInsert: null seq!" );
  147.   ifdebug(DPP, D, ProfileOn("DbInsert"));
  148.   debug6(DBS, D, "DbInsert(%s, %s, %s, %s, %s, %s, dfpos)",
  149.     string(db), bool(gall), SymName(sym), tag, seq,
  150.     dfnum == NO_FILE ? AsciiToFull(".") : FileName(dfnum));
  151.   if( reading(db) )  Error(INTERN, &fpos(db), "insert into reading database!");
  152.   if( filep(db) == null )
  153.   { if( StringLength(string(db)) + StringLength(NEW_INDEX_SUFFIX) >= MAX_LINE )
  154.       Error(FATAL, no_fpos, "database file name %s%s is too long",
  155.     string(db), NEW_INDEX_SUFFIX);
  156.     StringCopy(buff, string(db));
  157.     StringCat(buff, NEW_INDEX_SUFFIX);
  158.     filep(db) = StringFOpen(buff, "w");
  159.     if( filep(db) == null )
  160.       Error(FATAL, &fpos(db), "cannot write to database file %s", buff);
  161.   }
  162.   if( dfnum != NO_FILE )
  163.   { StringCopy(buff, FileName(dfnum));
  164.     StringCopy(&buff[StringLength(buff)-StringLength(DATA_SUFFIX)], STR_EMPTY);
  165.   }
  166.   else StringCopy(buff, AsciiToFull("."));
  167.   SymToNum(db, sym, symnum, gall);
  168.   ifdebug(DBS, D,
  169.   fprintf(stderr, "  -> %s%d&%s\t%s\t%ld\t%s\n", gall ? "&" : "", symnum,
  170.     tag, seq, dfpos, buff);
  171.   );
  172.   fprintf(filep(db), "%s%d&%s\t%s\t%ld\t%s\n", gall ? "&" : "", symnum,
  173.     tag, seq, dfpos, buff);
  174.   debug0(DBS, D, "DbInsert returning.");
  175.   ifdebug(DPP, D, ProfileOff("DbInsert"));
  176. } /* end DbInsert */
  177.  
  178.  
  179. /*@::DbConvert(), DbClose()@**************************************************/
  180. /*                                                                           */
  181. /*  DbConvert(db, full_name)                                                 */
  182. /*                                                                           */
  183. /*  Convert database db from writable to readable, then dispose it.          */
  184. /*  full_name is TRUE if symbols are to be known by their full path name.    */
  185. /*                                                                           */
  186. /*****************************************************************************/
  187.  
  188. DbConvert(db, full_name)
  189. OBJECT db;  BOOLEAN full_name;
  190. { FULL_CHAR oldname[MAX_LINE+10], newname[MAX_LINE];
  191.   char buff[2*MAX_LINE + 20];
  192.   OBJECT link, y;
  193.   ifdebug(DPP, D, ProfileOn("DbConvert"));
  194.   debug2(DBS, D, "DbConvert( %d %s )", (int) db,string(db));
  195.   if( reading(db) )  Error(INTERN, &fpos(db), "DbConvert: reading database!");
  196.   StringCopy(newname, string(db));
  197.   StringCat(newname, INDEX_SUFFIX);
  198.   StringCopy(oldname, string(db));
  199.   StringCat(oldname, NEW_INDEX_SUFFIX);
  200.   if( filep(db) != null )
  201.   { for( link = Down(db);  link != db;  link = NextDown(link) )
  202.     { Child(y, link);
  203.       assert( type(y) == CROSS_SYM || type(y) == ACAT, "DbConvert: y!" );
  204.       if( type(y) != CROSS_SYM )  continue;
  205.       fprintf(filep(db), "%s %d %s\n",
  206.     db_targ(link) ? "#target" : "#symbol",
  207.     number(link),
  208.     full_name ? FullSymName(symb(y), AsciiToFull(" ")) : SymName(symb(y)));
  209.     }
  210.     fclose(filep(db));
  211. #ifndef OSK
  212.     sprintf(buff, "sort -o %s %s", newname, oldname);
  213. #else
  214.     sprintf(buff, "sort >-%s %s", newname, oldname);
  215. #endif
  216.     system(buff);
  217.   }
  218.   else StringUnlink(newname);
  219.   StringUnlink(oldname);
  220.   DeleteNode(db);
  221.   debug0(DBS, D, "DbConvert returning.");
  222.   ifdebug(DPP, D, ProfileOff("DbConvert"));
  223. } /* end DbConvert */
  224.  
  225.  
  226. /*****************************************************************************/
  227. /*                                                                           */
  228. /*  DbClose(db)                                                              */
  229. /*                                                                           */
  230. /*  Close readable database db.                                              */
  231. /*                                                                           */
  232. /*****************************************************************************/
  233.  
  234. DbClose(db)
  235. OBJECT db;
  236. { if( db != nil && filep(db) != NULL )
  237.   {  fclose(filep(db));
  238.      filep(db) = NULL;
  239.   }
  240. } /* end DbClose */
  241.  
  242.  
  243. /*@::DbLoad()@****************************************************************/
  244. /*                                                                           */
  245. /*  OBJECT DbLoad(stem, fpath, create, symbs)                                */
  246. /*                                                                           */
  247. /*  Open for reading the database whose index file name is string(stem).li.  */
  248. /*  This file has not yet been defined; its search path is fpath.  If it     */
  249. /*  will not open and create is true, try creating it from string(stem).ld.  */
  250. /*                                                                           */
  251. /*  symbs is an ACAT of CLOSUREs showing the symbols that the database may   */
  252. /*  contain; or nil if the database may contain any symbol.                  */
  253. /*                                                                           */
  254. /*****************************************************************************/
  255.  
  256. OBJECT DbLoad(stem, fpath, create, symbs)
  257. OBJECT stem;  int fpath;  BOOLEAN create;  OBJECT symbs;
  258. { FILE *fp;  OBJECT db, t, res, tag, par, sym, link, y;
  259.   int i, lnum, num, count;  FILE_NUM index_fnum, dfnum;  long dfpos;
  260.   BOOLEAN gall;  FULL_CHAR line[MAX_LINE], sym_name[MAX_LINE];
  261.   ifdebug(DPP, D, ProfileOn("DbLoad"));
  262.   debug3(DBS, D, "DbLoad(%s, %d, %s, -)", string(stem), fpath, bool(create));
  263.  
  264.   /* open or else create index file fp */
  265.   index_fnum = DefineFile(string(stem), INDEX_SUFFIX, &fpos(stem), INDEX_FILE,
  266.          fpath);
  267.   fp = OpenFile(index_fnum, create, FALSE);
  268.   if( fp == null && create )
  269.   { db = nil;
  270.     dfnum = DefineFile(string(stem), DATA_SUFFIX, &fpos(stem),
  271.       DATABASE_FILE, DATABASE_PATH);
  272.     dfpos = 0L;  LexPush(dfnum, 0, DATABASE_FILE);  t = LexGetToken();
  273.     while( type(t) == LBR )
  274.     { res = Parse(&t, StartSym, FALSE, FALSE);
  275.       if( t != nil || type(res) != CLOSURE )  Error(FATAL, &fpos(res),
  276.     "syntax error in database file %s", FileName(dfnum));
  277.       assert( symbs != nil, "DbLoad: create && symbs == nil!" );
  278.       if( symbs != nil )
  279.       {    for( link = Down(symbs);  link != symbs;  link = NextDown(link) )
  280.     { Child(y, link);
  281.       if( type(y) == CLOSURE && actual(y) == actual(res) )  break;
  282.     }
  283.     if( link == symbs )  Error(FATAL, &fpos(res),
  284.       "%s found in database but not declared in %s line",
  285.       SymName(actual(res)), KW_DATABASE);
  286.       }
  287.       for( tag = nil, link = Down(res);  link != res;  link = NextDown(link) )
  288.       {    Child(par, link);
  289.     if( type(par) == PAR && is_tag(actual(par)) && Down(par) != par )
  290.     { Child(tag, Down(par));
  291.       break;
  292.     }
  293.       }
  294.       if( tag == nil )
  295.     Error(FATAL, &fpos(res), "database symbol %s has no tag", SymName(res));
  296.       tag = ReplaceWithTidy(tag);
  297.       if( !is_word(type(tag)) )
  298.     Error(FATAL, &fpos(res), "database symbol tag is not a simple word");
  299.       if( StringEqual(string(tag), STR_EMPTY) )
  300.     Error(FATAL, &fpos(res), "database symbol tag is an empty word");
  301.       if( db == nil )
  302.       {    StringCopy(line, FileName(dfnum));
  303.     i = StringLength(line) - StringLength(INDEX_SUFFIX);
  304.     assert( i > 0, "DbLoad: FileName(dfnum) (1)!" );
  305.     StringCopy(&line[i], STR_EMPTY);
  306.     db = DbCreate(MakeWord(WORD, line, &fpos(stem)));
  307.       }
  308.       DbInsert(db, FALSE, actual(res), string(tag), STR_ZERO, NO_FILE, dfpos);
  309.       DisposeObject(res);  dfpos = LexNextTokenPos();  t = LexGetToken();
  310.     }
  311.     if( type(t) != END )
  312.       Error(FATAL, &fpos(t), "%s or end of file expected here", KW_LBR);
  313.     LexPop();
  314.     if( db == nil )
  315.     { StringCopy(line, FileName(dfnum));
  316.       i = StringLength(line) - StringLength(INDEX_SUFFIX);
  317.       assert( i > 0, "DbLoad: FileName(dfnum) (2)!" );
  318.       StringCopy(&line[i], STR_EMPTY);
  319.       db = DbCreate(MakeWord(WORD, line, &fpos(stem)));
  320.     }
  321.     DbConvert(db, FALSE);
  322.     if( (fp = OpenFile(index_fnum, FALSE, FALSE)) == null )
  323.       Error(FATAL, &fpos(db), "cannot open database file %s",
  324.       FileName(index_fnum));
  325.   }
  326.  
  327.   /* set up database record */
  328.   StringCopy(line, FileName(index_fnum));
  329.   i = StringLength(line) - StringLength(INDEX_SUFFIX);
  330.   assert( i > 0, "DbLoad: FileName(index_fnum)!" );
  331.   StringCopy(&line[i], STR_EMPTY);
  332.   db = MakeWord(WORD, line, &fpos(stem));
  333.   reading(db) = TRUE;  filep(db) = fp;
  334.   if( symbs != nil )
  335.   { assert( type(symbs) = ACAT, "DbLoad: type(symbs)!" );
  336.     Link(db, symbs);
  337.   }
  338.   if( fp == null )
  339.   { debug1(DBS, D, "DbLoad returning (empty) %s", string(db));
  340.     ifdebug(DPP, D, ProfileOff("DbLoad"));
  341.     return db;
  342.   }
  343.  
  344.   /* read header lines of index file, find its symbols */
  345.   left_pos(db) = 0;  lnum = 0;
  346.   while( StringFGets(line, MAX_LINE, fp) != NULL && line[0] == '#' )
  347.   { lnum++;
  348.     left_pos(db) = (int) ftell(fp);
  349.     gall = StringBeginsWith(line, AsciiToFull("#target "));
  350.     sscanf( (char *) line, gall ? "#target %d" : "#symbol %d", &num);
  351.     for( i = 8;  line[i] != CH_SPACE && line[i] != '\0';  i++ );
  352.     if( symbs == nil )
  353.     {
  354.       /* any symbols are possible, full path names in index file required */
  355.       count = 0;  sym = StartSym;
  356.       while( line[i] != CH_NEWLINE && line[i] != '\0' )
  357.       {    PushScope(sym, FALSE, FALSE);  count++;
  358.     sscanf( (char *) &line[i+1], "%s", sym_name);
  359.     sym = SearchSym(sym_name, StringLength(sym_name));
  360.     i += StringLength(sym_name) + 1;
  361.       }
  362.       for( i = 1;  i <= count;  i++ )  PopScope();
  363.     }
  364.     else
  365.     {
  366.       /* only symbs symbols possible, full path names not required */
  367.       sym = nil;
  368.       sscanf( (char *) &line[i+1], "%s", sym_name);
  369.       for( link = Down(symbs);  link != symbs;  link = NextDown(link) )
  370.       {    Child(y, link);
  371.     assert( type(y) == CLOSURE, "DbLoad: type(y) != CLOSURE!" );
  372.     if( StringEqual(sym_name, SymName(actual(y))) )
  373.     { sym = actual(y);
  374.       break;
  375.     }
  376.       }
  377.     }
  378.     if( sym != nil && sym != StartSym )
  379.     { if( cross_sym(sym) == nil )  CrossInit(sym);
  380.       link = Link(db, cross_sym(sym));
  381.       number(link) = num;  db_targ(link) = gall;
  382.       if( gall )  is_extern_target(sym) = uses_extern_target(sym) = TRUE;
  383.     }
  384.     else
  385.     { Error(WARN, &fpos(db), "undefined symbol in database file %s (line %d)",
  386.             FileName(index_fnum), lnum);
  387.       debug1(DBS, D, "DbLoad returning %s (error)", string(db));
  388.       fclose(filep(db));  filep(db) = null;  /* effectively an empty database */
  389.       ifdebug(DPP, D, ProfileOff("DbLoad"));
  390.       return db;
  391.     }
  392.   }
  393.   debug1(DBS, D, "DbLoad returning %s", string(db));
  394.   ifdebug(DPP, D, ProfileOff("DbLoad"));
  395.   return db;
  396. } /* end DbLoad */
  397.  
  398.  
  399. /*@::SearchFile()@************************************************************/
  400. /*                                                                           */
  401. /*  static BOOLEAN SearchFile(fp, left, right, str, line)                    */
  402. /*                                                                           */
  403. /*  File fp is a text file.  left is the beginning of a line, right is the   */
  404. /*  end of a line.   Search the file by binary search for a line beginning   */
  405. /*  with str.  If found, return it in line, else return FALSE.               */
  406. /*                                                                           */
  407. /*****************************************************************************/
  408.  
  409. static BOOLEAN SearchFile(fp, left, right, str, line)
  410. FILE *fp;  int left, right;  FULL_CHAR *str, *line;
  411. { int l, r, mid, mid_end;  FULL_CHAR buff[MAX_LINE];  BOOLEAN res;
  412.   ifdebug(DPP, D, ProfileOn("SearchFile"));
  413.   debug3(DBS, DD, "SearchFile(fp, %d, %d, %s, line)", left, right, str);
  414.  
  415.   l = left;  r = right;
  416.   while( l <= r )
  417.   {
  418.     /* loop invt: (l==0 or fp[l-1]==CH_NEWLINE) and (fp[r] == CH_NEWLINE)    */
  419.     /* and first key >= str lies in the range fp[l..r+1]                     */
  420.  
  421.     /* find line near middle of the range; mid..mid_end brackets it */
  422.     debug2(DBS, DD, "  start loop: l = %d, r = %d", l, r);
  423.     mid = (l + r)/2;
  424.     fseek(fp, (long) mid, 0);
  425.     do { mid++; } while( getc(fp) != CH_NEWLINE );
  426.     if( mid == r + 1 )
  427.     { mid = l;
  428.       fseek(fp, (long) mid, 0);
  429.     }
  430.     StringFGets(line, MAX_LINE, fp);
  431.     mid_end = (int) ftell(fp) - 1;
  432.     debug3(DBS, DD, "  mid: %d, mid_end: %d, line: %s", mid, mid_end, line);
  433.     assert( l <= mid,      "SearchFile: l > mid!"        );
  434.     assert( mid < mid_end, "SearchFile: mid >= mid_end!" );
  435.     assert( mid_end <= r,  "SearchFile: mid_end > r!"    );
  436.  
  437.     /* compare str with this line and prepare next step */
  438.     debug2(DBS, DD, "  comparing key %s with line %s", str, line);
  439.     if( StringLessEqual(str, line) )  r = mid - 1;
  440.     else l = mid_end + 1;
  441.   } /* end while */
  442.  
  443.   /* now first key >= str lies in fp[l]; compare it with str */
  444.   if( l < right )
  445.   { fseek(fp, (long) l, 0);
  446.     StringFGets(line, MAX_LINE, fp);
  447.     sscanf( (char *) line, "%s", buff);
  448.     res = StringEqual(str, buff);
  449.   }
  450.   else res = FALSE;
  451.   debug1(DBS, DD, "SearchFile returning %s", bool(res));
  452.   ifdebug(DPP, D, ProfileOff("SearchFile"));
  453.   return res;
  454. } /* end SearchFile */
  455.  
  456.  
  457. /*@::DbRetrieve()@************************************************************/
  458. /*                                                                           */
  459. /*  BOOLEAN DbRetrieve(db, gall, sym, tag, seq, dfnum, dfpos, cont)          */
  460. /*                                                                           */
  461. /*  Retrieve the first entry of database db with the given gall, sym and     */
  462. /*  tag.  Set *seq, *dfnum, *dfpos to the associated value.                  */
  463. /*  Set *cont to a private value for passing to DbRetrieveNext.              */
  464. /*                                                                           */
  465. /*****************************************************************************/
  466.  
  467. BOOLEAN DbRetrieve(db, gall, sym, tag, seq, dfnum, dfpos, cont)
  468. OBJECT db;  BOOLEAN gall;  OBJECT sym;  FULL_CHAR *tag, *seq;
  469. FILE_NUM *dfnum;  long *dfpos;  long *cont;
  470. { int symnum;  FULL_CHAR line[MAX_LINE], buff[MAX_LINE];  OBJECT y;
  471.   ifdebug(DPP, D, ProfileOn("DbRetrieve"));
  472.   debug4(DBS, D, "DbRetrieve(%s, %s%s&%s)", string(db), gall ? "&" : "",
  473.     SymName(sym), tag);
  474.   if( !reading(db) || filep(db) == null )
  475.   { debug0(DBS, D, "DbRetrieve returning FALSE (empty or not reading)");
  476.     ifdebug(DPP, D, ProfileOff("DbRetrieve"));
  477.     return FALSE;
  478.   }
  479.   SymToNum(db, sym, symnum, FALSE);
  480.   sprintf( (char *) buff, "%s%d&%s", gall ? "&" : "", symnum, tag);
  481.   fseek(filep(db), 0L, 2);
  482.   if( !SearchFile(filep(db), (int) left_pos(db), (int) ftell(filep(db)) - 1,
  483.     buff, line) )
  484.   { debug0(DBS, D, "DbRetrieve returning FALSE (key not present)");
  485.     ifdebug(DPP, D, ProfileOff("DbRetrieve"));
  486.     return FALSE;
  487.   }
  488.   sscanf( (char *) line, "%*s\t%s\t%ld\t%[^\n]", seq, dfpos, buff);
  489.   if( StringEqual(buff, AsciiToFull(".")) )
  490.   { StringCopy(buff, string(db));
  491.   }
  492.   *dfnum = FileNum(buff, DATA_SUFFIX);
  493.   if( *dfnum == NO_FILE )  /* can only occur in cross reference database */
  494.     *dfnum = DefineFile(buff, DATA_SUFFIX, &fpos(db),
  495.       DATABASE_FILE, SOURCE_PATH);
  496.   *cont = ftell(filep(db));
  497.   Child(y, Down(db));
  498.   debug2(DBS, D, "DbRetrieve returning TRUE (in %s at %ld)",
  499.     FileName(*dfnum), *dfpos);
  500.   ifdebug(DPP, D, ProfileOff("DbRetrieve"));
  501.   return TRUE;
  502. } /* end DbRetrieve */
  503.  
  504.  
  505. /*@::DbRetrieveNext()@********************************************************/
  506. /*                                                                           */
  507. /*  BOOLEAN DbRetrieveNext(db, gall, sym, tag, seq, dfnum, dfpos, cont)      */
  508. /*                                                                           */
  509. /*  Retrieve the entry of database db pointed to by *cont.                   */
  510. /*  Set *gall, *sym, *tag, *seq, *dfnum, *dfpos to the value of the entry.   */
  511. /*  Reset *cont to the next entry for passing to the next DbRetrieveNext.    */
  512. /*                                                                           */
  513. /*****************************************************************************/
  514.  
  515. BOOLEAN DbRetrieveNext(db, gall, sym, tag, seq, dfnum, dfpos, cont)
  516. OBJECT db;  BOOLEAN *gall;  OBJECT *sym;  FULL_CHAR *tag, *seq;
  517. FILE_NUM *dfnum;  long *dfpos;  long *cont;
  518. { FULL_CHAR line[MAX_LINE], fname[MAX_LINE]; int symnum;
  519.   ifdebug(DPP, D, ProfileOn("DbRetrieveNext"));
  520.   debug2(DBS, D, "DbRetrieveNext( %s, %ld )", string(db), *cont);
  521.   if( !reading(db) )  Error(INTERN, &fpos(db), "DbRetrieveNext: writing!");
  522.   if( filep(db) == null )
  523.   { debug0(DBS, D, "DbRetrieveNext returning FALSE (empty database)");
  524.     ifdebug(DPP, D, ProfileOff("DbRetrieveNext"));
  525.     return FALSE;
  526.   }
  527.   fseek(filep(db), *cont == 0L ? (long) left_pos(db) : *cont, 0);
  528.   if( StringFGets(line, MAX_LINE, filep(db)) == NULL )
  529.   { debug0(DBS, D, "DbRetrieveNext returning FALSE (no successor)");
  530.     ifdebug(DPP, D, ProfileOff("DbRetrieveNext"));
  531.     return FALSE;
  532.   }
  533.   *gall = (line[0] == '&' ? 1 : 0);
  534.   sscanf( (char *) &line[*gall], "%d&%s\t%s\t%ld\t%[^\n]",
  535.     &symnum, tag, seq,dfpos,fname);
  536.   if( StringEqual(fname, AsciiToFull(".")) )
  537.   { StringCopy(fname, string(db));
  538.   }
  539.   *dfnum = FileNum(fname, DATA_SUFFIX);
  540.   if( *dfnum == NO_FILE )  /* can only occur in cross reference database */
  541.     *dfnum = DefineFile(fname, DATA_SUFFIX, &fpos(db),
  542.       DATABASE_FILE, SOURCE_PATH);
  543.   NumToSym(db, symnum, *sym);  *cont = ftell(filep(db));
  544.   debug2(DBS, D, "DbRetrieveNext returning TRUE (in %s at %ld)",
  545.     FileName(*dfnum), *dfpos);
  546.   ifdebug(DPP, D, ProfileOff("DbRetrieveNext"));
  547.   return TRUE;
  548. } /* end DbRetrieveNext */
  549.