NCBI C Toolkit Cross Reference

C/cdromlib/cdnewlib.c


  1 /* cdnewlib.c
  2  * ===========================================================================
  3  *
  4  *                            PUBLIC DOMAIN NOTICE                          
  5  *               National Center for Biotechnology Information
  6  *                                                                          
  7  *  This software/database is a "United States Government Work" under the   
  8  *  terms of the United States Copyright Act.  It was written as part of    
  9  *  the author's official duties as a United States Government employee and 
 10  *  thus cannot be copyrighted.  This software/database is freely available 
 11  *  to the public for use. The National Library of Medicine and the U.S.    
 12  *  Government have not placed any restriction on its use or reproduction.  
 13  *                                                                          
 14  *  Although all reasonable efforts have been taken to ensure the accuracy  
 15  *  and reliability of the software and data, the NLM and the U.S.          
 16  *  Government do not and cannot warrant the performance or results that    
 17  *  may be obtained by using this software or data. The NLM and the U.S.    
 18  *  Government disclaim all warranties, express or implied, including       
 19  *  warranties of performance, merchantability or fitness for any particular
 20  *  purpose.                                                                
 21  *                                                                          
 22  *  Please cite the author in any work or product based on this material.   
 23  *
 24  * ===========================================================================
 25  *
 26  * RCS $Id: cdnewlib.c,v 6.2 1999/03/12 18:44:57 kans Exp $
 27  *
 28  * Author:  Gregory Schuler
 29  *
 30  * Version Creation Date: 08-22-94
 31  *
 32  * File Description:  Entrez low-level interface to CD-ROMs
 33  *
 34  * Modifications:  
 35  * --------------------------------------------------------------------------
 36  * Rev   Date      Name      Description of modification
 37  * ----  --------  --------  ------------------------------------------------
 38  * 1.1   08-22-94  Schuler   Initial revision
 39  * 1.2   08-22-94  Schuler   Rearranged the #include directives
 40  * 1.3   08-25-94  Schuler   cd3_CdGetDocSum: fix for new *.sum format.
 41  * 1.3   08-25-94  Schuler   Added error codes to ErrPostEx in many places.
 42  * 1.4   08-25-94  Kans      Some typecasts to make compilers happy
 43  * 1.5   08-26-94  Schuler   Some typecasts to make compilers happy
 44  * 1.6   08-28-94  Ostell    Convert double-slash comment to slash-star
 45  * 1.7   08-31-94  Schuler   Fixed bug in CdTrmLookup
 46  * 1.8   08-31-94  Schuler   Added magic number/version number checking
 47  * 1.9   09-01-94  Schuler   Use defines for directory names throughout
 48  * 1.10  09-01-94  Kans      Fixed bug in CdEntrez_FileOpen
 49  * 1.11  09-02-94  Schuler   Fixed bug in TrmIndex_Destruct
 50  * 1.12  09-02-94  Schuler   Implemented the CdMap_Destruct function 
 51  * 1.13  09-05-94  Schuler   Implemented the ReadCdLayout function; Fixed bug
 52  *                           in CdEnumFiles; Fixed bug in CdEntrez_FileOpen
 53  * 1.14  09-05-94  Schuler   Fixed bug in CdTrmLookup
 54  * 1.15  09-05-94  Schuler   Fixed bug in CdTrmLookup (tricky!)
 55  * 1.16  09-06-94  Schuler   Added some more VERBOSEs and ASSERTs
 56  * 1.17  09-09-94  Schuler   Fixed bug in CD swapping logic
 57  * 1.18  09-21-94  Schuler   Fixed bug in CdFini (NULL out _huff_tab)
 58  * 1.19  09-22-94  Schuler   Another fix to swapping logic...
 59  * 1.20  10-04-94  Schuler   Fixed invalid redeclaration of SwapInt4
 60  * 1.21 10-08-94  ?         ?
 61  * 1.22  10-08-94  Kans      Eliminate prompt to insert CD when already there
 62  * 1.23  10-10-94  Schuler   Fixed bug in CdEnumFiles (wanted Entrez3 always)
 63  * 1.24  10-24-94  Schuler   Fixed CdDevice_FileOpen for to respect the
 64  *                           upper_case and semicolon_one flags.
 65  * 1.25  10-25-94  Schuler   Change to CdVolume_Mount to re-init settings
 66  *                           of upper_case and semicolon_one
 67  * 1.26  10-26-94  Schuler   Changed "long" to "Int4" in a couple places
 68  * 1.27  11-17-94  Schuler   Fixed bug in ReadCdLayout 
 69  * 1.28  11-17-94  Schuler   Fixed bug in FindFloatingEntrezVolumes
 70  * 1.29  11-17-94  Schuler   Scan for already-mounted Entrez CDs on
 71  *                           Mac desktop at startup
 72  * 1.30  11-17-94  Schuler   Fixed CdTestPath to properly handle cases where
 73  *                           CD-ROM files are upper case and case matters
 74  * 1.31  11-18-94  Schuler   Added verbose trace message logging
 75  * 1.32  11-19-94  Schuler   Plugged some memory leaks
 76  * 1.33  11-21-94  Schuler   Fixed FileReadSwap4 to support 8-byte longs
 77  * 1.34  11-22-94  Schuler   Changed arg in cd3_CdLinkUidGet fom long to DocUid
 78  * 1.35  11-22-94  Schuler   Fixed invalid redeclaration of SwapInt4
 79  * 1.36  11-22-94  Schuler   Changed "long" to "DocUid" in CdLinkUidGet
 80  * 1.37  11-25-94  Schuler   Fixed bug in UidIndex_ReadPage (re: 8-byte longs)
 81  * 1.38  11-30-94  Schuler   Populate new cd_count field EntrezInfo
 82  * 1.39  01-04-95  Kans      ?
 83  * 1.40  01-20-95  Schuler   Added the CdMountEntrezVolume() and 
 84  *                           CdUnmountEntrezVolume() functions.
 85  * 1.41  02-27-95  Schuler   CdVolume_Mount() calls CdDevice_Init() if needed
 86  * 1.42  02-28-95  Schuler   Added more verbose log messages
 87  * 1.43  02-28-95  Schuler   Plugged some memory leaks
 88  * 1.44  03-02-95  Schuler   Changed fopen/fclose to FileOpen/FileClose
 89  *
 90  * 05-19-95 Schuler     Added rcs Log directive for automatic insertion of
 91  *                      modification comments.
 92  *
 93  * Revision $Log: cdnewlib.c,v $
 94  * Revision Revision 6.2  1999/03/12 18:44:57  kans
 95  * Revision fixed ErrPostEx problem
 96  * Revision
 97  * Revision Revision 6.1  1998/08/24 18:42:16  kans
 98  * Revision fixed -v -fd warnings
 99  * Revision
100  * Revision Revision 6.0  1997/08/25 18:13:01  madden
101  * Revision Revision changed to 6.0
102  * Revision
103  * Revision Revision 5.3  1997/06/26 21:55:38  vakatov
104  * Revision [PC] DLL'd "ncbicdr.lib", "ncbiacc.lib", "ncbinacc.lib" and "ncbicacc.lib"
105  * Revision
106  * Revision Revision 5.2  1997/02/14 22:34:49  epstein
107  * Revision allocated more memory for detailed info
108  * Revision
109  * Revision 5.1  1996/11/19  21:46:45  shavirin
110  * CdInit() CdFini() messages made optional (printed in
111  * verbose mode)
112  *
113  * Revision 5.0  1996/05/28  13:55:34  ostell
114  * Set to revision 5.0
115  *
116  * Revision 4.2  1996/04/02  19:02:49  epstein
117  * change CDVOL_MAX and its use
118  *
119  * Revision 4.1  1996/03/26  16:29:11  epstein
120  * migrate byte-swapping functions to ncbimisc.[ch]
121  *
122  * Revision 4.0  1995/07/26  13:50:32  ostell
123  * force revision to 4.0
124  *
125  * Revision 1.48  1995/06/05  21:32:56  kans
126  * CdEnumFiles now uses safe fgets version (UnixFileGets)
127  *
128  * Revision 1.47  1995/05/18  17:27:46  kans
129  * changed qsort to HeapSort to avoid corruption/hang on one platform
130  *
131  * Revision 1.46  1995/05/16  14:36:20  schuler
132  * Automatic comment insertion enabled
133  *
134  *
135  * ==========================================================================
136  */
137 
138 #define REVISION_STR "$Revision: 6.2 $"
139 
140 
141 
142 /*
143 CONFIGURATION NOTES
144 
145 [CdEntrez]
146         
147 DeviceCount = (number)
148                 This should be equal to the number of Entrez CD-ROM images
149                 that may be simultaneosuly on-line, either as mounted CD-ROMs
150                 or as hard disk images.  For example, suppose that you have a
151                 single CD-ROM drive on which you keep one of the Entrez CD-ROMs
152                 mounted plus a huge hard disk onto which you have copied the 
153                 contents of the remaining two CD-ROMs.  In this case, the 
154                 DeviceCount setting should be 3.
155                 
156 IdxCacheSize = (kbytes)
157 TrmCacheSize = (kbytes)
158                 Maximum amount of memory to use for caching index & term files
159                 
160 LogVerbose = { Yes / No }
161             Causes verbose logging of status information for use in debugging
162             (default is No).  Note that error logging must be enabled by the
163             application for anything to get logged.
164             
165 
166 [CdEntrez.device.#]            (one section for each device, # = 1,2,3..)
167         
168 Type = { CD / HARDDISK / NET }
169                 Type of device: CD-ROM drive, local hard drive, or network drive.
170                 
171 Formal_Name = (some string)
172                         This setting is optional, but provides a name for the device (e.g.
173                         "Second CD-ROM drive") that can be used in user messages.
174 
175 Root = (full path)
176                 Full path corresponding to the root of the CD-ROM (excluding
177                 the volume name when used with Insert_Volname=Yes; see below).
178                 On the Macintosh, this should be omitted or left empty for
179                 EJECTABLE devices with Insert_Volname=Yes.
180                 
181 Insert_Volname = { Yes / No }
182                 If Yes, the name of the volume will be inserted after the
183                 string specified in the Root setting and before the names of
184                 subdirectories on the CD.  This would be used for EJECTABLE
185                 devices on Macintosh and Solaris systems (on the Mac, the
186                 Root setting should be emtpy in this case as the path starts
187                 with the volume name).
188                 
189 Ejectable = { Yes / No }
190                 Set to yes if the device should be considered ejectable.
191                 
192 Bind = { Entrez1 / Entrez2 / Entrez 3 }
193                 Use this setting to bind an Entrez CD-ROM to a device.  The
194                 setting should be the volume name of the CD: "Entrez1", "Entrez2",
195                 etc. (not sensitive to case).  Bound volumes are never ejected,
196                 even if Ejectable=Yes. 
197 
198 Device_Name =
199 Raw_Device_Name =
200 Mount_Point
201 Mount_Cmd =
202             These are some parameters to be passed to the MountCd and
203             EjectCd functions.  They may be required on certain UNIX
204             and VMS systems.
205             
206 */
207 
208 #include <cdromlib.h>
209 
210 #ifdef _NEW_CdEntrez_
211 
212 
213 static char _this_module[] = "CdEntrez";
214 #undef  THIS_MODULE
215 #define THIS_MODULE _this_module
216 static char _this_file[] = __FILE__;
217 #undef  THIS_FILE
218 #define THIS_FILE _this_file
219 
220 #undef VERBOSE
221 #define VERBOSE   if(_verbose_trace) Nlm_ErrLogPrintf
222 
223 #define CDVOL_MAX  8
224 #define TYPE_MAX   DocType_MAX
225 #define DIV_MAX    64
226 #define FLD_MAX    32
227 
228 #define TYPE_TAG_LEN  2
229 #define DIV_TAG_LEN   3
230 #define FLD_TAG_LEN   4
231 
232 #define DEFAULT_IdxCacheSize  16
233 #define DEFAULT_SumCacheSize  16
234 #define DEFAULT_TrmCacheSize   8
235 
236 
237 #define VOLNUM_IS_VALID(x) ((x)<=_volume_ct && (x)>0)
238 #define TYPE_IS_VALID(x)   ((x)<_type_ct && (x)>=0)
239 #define FIELD_IS_VALID(x)  ((x)<_fld_ct && (x)>=0)
240 
241 #define TYPTAG(x)  _cdinfo->type_info[x].tag
242 #define FLDTAG(x)  _cdinfo->field_info[x].tag
243 #define DIVTAG(x)  _cdinfo->div_info[x].tag
244 
245 #define HCA 1
246 #define SUM 2
247 #define LNK 3
248 #define OFS 4
249 #define OIX 5
250 #define TRM 6
251 #define TIX 7
252 #define PST 8
253 #define INF 9
254 
255 #define MAGIC_hca 0x11BD
256 #define MAGIC_sum 0x0025
257 #define MAGIC_lnk 0x0A02
258 #define MAGIC_ofs 0x0A03
259 #define MAGIC_oix 0x0A04
260 #define MAGIC_trm 0x0A05
261 #define MAGIC_tix 0x0A06
262 #define MAGIC_pst 0x0A07
263 #define MAGIC_inf 0x0A0A
264 
265 #define FORMAT_hca 2
266 #define FORMAT_sum 2
267 #define FORMAT_ofs 2
268 #define FORMAT_oix 2
269 #define FORMAT_trm 2
270 #define FORMAT_tix 2
271 #define FORMAT_pst 2
272 #define FORMAT_lnk 2
273 #define FORMAT_inf 1
274 
275 /* ========== CdDevice class ========== */
276 struct CdVolume;
277 
278 typedef struct CdDevice 
279 {
280         CdDevInfo inf;               /* device characteristics  */
281         int   bound_cd;              /* Entrez volume number bound to this device, if any */
282         int   hint;                  /* used during initialization for transient bind */
283         struct CdVolume *volume;     /* Entrez volume currently mounted on this device */
284         unsigned is_cdrom      :1;   /* TRUE if device is a CD-ROM reader */
285         unsigned is_ejectable  :1;   /* TRUE if device should be considered ejectable */
286         unsigned ins_volname   :1;   /* TRUE if the volume name gets inserted into path */
287         unsigned semicolon_one :1;   /* TRUE if the version number ";1" should be appended */
288         unsigned upper_case    :1;   /* TRUE if the file name should be upper case */
289         unsigned is_inited     :1;   /* TRUE if semicolon_one and upper_case are known */
290         time_t timestamp;            /* time this device was last used           */
291 }
292 CdDevice;
293 
294 #define CdDevice_New(a)  CdDevice_Construct(MemNew(sizeof(CdDevice)),a) 
295 #define CdDevice_Free(a) MemFree((void*)CdDevice_Destruct(a))
296 static CdDevice* CdDevice_Construct (CdDevice *cddev, int num);
297 static CdDevice* CdDevice_Destruct (CdDevice *cddev);
298 static int   CdDevice_Init (CdDevice *cddev);
299 static int CdDevice_FileBuildPath (CdDevice *cddev, char *fpath, const char *fdir, const char *fname);
300 static FILE* CdDevice_FileOpen (CdDevice *cddev, const char *fdir, const char *fname, const char *fmode);
301 static void  CdDevice_Touch (CdDevice *cddev);
302 static CdDevice* GetLruDevice (void);
303 
304 /* ==========  CdVolume class ========== */
305 
306 typedef struct CdVolume
307 {
308         char *volume_name;   /* "entrez1", "entrez2", or "entrez3"       */
309         short volume_num;    /* 1, 2, or 3                               */
310         CdDevice *device;    /* on which device is this volume mounted ? */
311         time_t timestamp;    /* time this volume was last used           */
312 }
313 CdVolume;
314 
315 #define CdVolume_New(a)  CdVolume_Construct(MemNew(sizeof(CdVolume)),a) 
316 #define CdVolume_Free(a) MemFree((void*)CdVolume_Destruct(a))
317 static CdVolume* CdVolume_Construct (CdVolume *cdvol, int number);
318 static CdVolume* CdVolume_Destruct (CdVolume *cdvol);
319 static CdDevice* CdVolume_Mount (CdVolume *cdvol, CdDevice *cddev, int verify);
320 static int  CdVolume_Unmount (CdVolume *cdvol);
321 static int  CdVolume_IsMounted (CdVolume *cdvol);
322 static int FindFloatingEntrezVolumes (int *outlist);
323 
324 
325 /* ==========  UidIndex class ========= */
326 
327 typedef struct UidIndex 
328 {
329         DocType  type;     /* Document type: TYP_ML, TYP_AA, etc */
330         long  uid_ct;      /* Number of UIDs in index (can be > number of docs) */
331         long  uid_min;     /* Smallest UID in index */
332         long  uid_max;     /* Largest UID in index */
333         int   pages;       /* number of index pages */
334         long *index;       /* array of 1st UID on each page */
335 }
336 UidIndex;
337 
338 typedef struct UidIndexRec
339 {
340         long uid;          /* UID of document */
341         int  divnum;       /* division number (lookup in EntrezInfo) */
342         int  filnum;       /* hca file segment number (from 1) */
343         long hca_offset;   /* offset into hca file for full ASN.1 record */
344         long sum_offset;   /* offset into sum file for summary */
345         long lnk_offset;   /* offset into lnk file for links */
346 }
347 UidIndexRec;
348 
349 #define UidIndex_New(a,b) UidIndex_Construct(MemNew(sizeof(UidIndex)),a,b)      
350 #define UidIndex_Free(a)  MemFree((void*)UidIndex_Destruct(a))
351 static UidIndex* UidIndex_Construct (UidIndex *uidx, DocType type, EntrezTypeData *info);
352 static UidIndex* UidIndex_Destruct (UidIndex *uidx);
353 static int   UidIndex_ReadPageMap (UidIndex *uidx);
354 static long* UidIndex_ReadPage (UidIndex *uidx, int pagenum);
355 static int   UidIndex_Lookup (UidIndex *uidx, DocUid uid, UidIndexRec *rec);
356 
357 
358 /* ==========  TrmIndex class ========== */
359 
360 typedef struct TrmIndex
361 {
362         struct TrmIndex *next;   /* next element in linked list */
363         char tag[6];        /* tag for field (e.g. "mesh") */
364         unsigned type  :6;  /* ??? */
365         unsigned fld   :6;  /* ??? */
366         unsigned style :4;  /* ??? */
367         long trm_ct;        /* term count */
368         int  page_ct;       /* page count */
369         int  page_size;     /* page size */
370         char **index;       /* array of 1st term on each page */
371 }
372 TrmIndex;
373 
374 #define TrmIndex_New(a,b,c,d) TrmIndex_Construct(MemNew(sizeof(TrmIndex)),a,b,c,d)      
375 #define TrmIndex_Free(a)  MemFree((void*)TrmIndex_Destruct(a))
376 static TrmIndex* TrmIndex_Construct (TrmIndex *tidx, int type, int fld, const char *tag, EntrezFieldData *info);
377 static TrmIndex* TrmIndex_Destruct (TrmIndex *tidx);
378 static int   TrmIndex_ReadPageMap (TrmIndex *tidx);
379 static Byte* TrmIndex_ReadPage (TrmIndex *tidx, int pagenum);
380 static int   TrmIndex_Lookup (TrmIndex *tidx, const char *stem);
381 static CdTerm* TrmIndex_GetCdTerm (TrmIndex *tidx, const char *term);
382 static int TrmIndex_ScanPages (TrmIndex *tidx, int start, int count, CdTermProc proc);
383 static FILE * TrmIndex_PostingsFileOpen (TrmIndex *tidx);
384 
385 static CdTerm* getcdtrm (TrmIndex *tidx, int pagenum, Byte **pptr);
386 static int trmcmp (const char *term1, const char *term2);
387 static int trmncmp (const char *term1, const char *term2, int n);
388 
389 
390 /* ==========  CdMap class ========= */
391 
392 typedef struct CdMap
393 {
394         unsigned user_set :1;  /* any user-supplied path set ? */
395         unsigned multicd  :1;  /* files exist on multiple CDs ? */
396         unsigned lnklist  :1;  /* list is a linked list ? (array if false) */
397         short array_size;      /* array size for list */
398         short id_num;          /* id_number for divisions */
399         short cd_num;          /* CD number */
400         short *cd_list;        /* list of CD numbers, if multicd */
401         char *user_path;       /* user-supplied path */
402         struct CdMap *list;    /* linked list or array */
403 }
404 CdMap;
405 
406 #define CdMap_New(a)   CdMap_Construct((CdMap*)MemNew(sizeof(CdMap)),a)
407 #define CdMap_Free(x)   MemFree((void*)CdMap_Destruct(x))
408 static CdMap* CdMap_Construct (CdMap *map, int list_size);
409 static CdMap* CdMap_Destruct (CdMap *map);
410 static void CdMap_ParseCdNums (CdMap *map, char *nums);
411 static void CdMap_FindPath (CdMap *map, const char *key);
412 static int CdMap_GetSpecs (CdMap *map, int *cdlist, char *fdir);
413 static CdMap* CdMap_GetChild (CdMap *map, int nkid);
414 
415 
416 static Boolean ReadCdLayout (FILE *fd);
417 
418 
419 /* ========== LSet  class ========== */
420 typedef struct DocLink
421 {
422         DocUid uid;
423         int wt;
424 }
425 DocLink;
426 
427 typedef struct LSet 
428 {
429         short sorc_type;
430         short dest_type;
431         int   link_max;
432         int   count;
433         int   slots;
434         DocLink *link;
435 }
436 LSet;
437 
438 #define LSet_New(a,b,c)  LSet_Construct(MemNew(sizeof(LSet)),a,b,c)     
439 #define LSet_Free(x)     MemFree((void*)LSet_Destruct(x))
440 static LSet* LSet_Construct (LSet *lset, short sorc_type, short dest_type, int link_max);
441 static LSet * LSet_Destruct (LSet *lset);
442 static int LSet_Read (LSet *lset, FILE *fd);
443 static LinkSet* LSet_Convert (LSet *lset);
444 
445 
446 /* ==========  HuffTable class ========== */
447 
448 typedef struct HuffTable
449 {
450         int count;
451         short *left;
452         short *right;
453 }
454 HuffTable;
455 
456 #define HuffTable_New(a)   HuffTable_Construct(MemNew(sizeof(HuffTable)),a)
457 #define HuffTable_Free(a)  MemFree((void*)HuffTable_Destruct(a))
458 static HuffTable* HuffTable_Construct (HuffTable *huff, int slots);
459 static HuffTable* HuffTable_Destruct (HuffTable *huff);
460 static HuffTable* HuffTable_Read (FILE *fd);
461 
462 
463 /* ========== DecompInfo class ========== */
464 typedef struct DecompInfo
465 {
466         AsnIo *aio;
467         FILE *fd;
468         HuffTable *huff;
469         unsigned int byte;
470         unsigned int mask;
471 }
472 DecompInfo;
473 
474 #define DecompInfo_New(a) DecompInfo_Construct(MemNew(sizeof(DecompInfo)),a)
475 #define DecompInfo_Free(a)  MemFree((void*)DecompInfo_Destruct(a))
476 static DecompInfo* DecompInfo_Construct (DecompInfo *info, HuffTable *huff);
477 /*static DecompInfo* DecompInfo_Destruct (DecompInfo *info);*/
478 #define DecompInfo_Destruct(info)  (info)
479 static AsnIo* DecompInfo_Attach (DecompInfo *info, FILE *fd);
480 static FILE* DecompInfo_Detach (DecompInfo *info);
481 
482 static Int2 LIBCALLBACK DecompReadProc (void *p, char *buff, Uint2 count);
483 
484 /* ==========  Cache class ========== */
485 #define Cache_MAGIC_VALUE 223445L                 
486 
487 typedef struct CachePage
488 {
489         long id;
490         int  lock;
491         void *data;
492 }
493 CachePage;
494 
495 typedef void (PASCAL *CacheDataFreeProc)(void *data);
496 static void PASCAL DefCacheDataFreeProc (void *data);
497 
498 typedef struct Cache 
499 {
500         long magic;
501         CachePage *page;
502         int  page_slots;
503         int  page_count;
504         long page_size;    /* optional */
505         long hits;
506         long misses;
507         CacheDataFreeProc fproc;
508 }
509 Cache;
510 
511 #define Cache_New(a,b) Cache_Construct(MemNew(sizeof(Cache)),a,b)       
512 #define Cache_Free(a)  MemFree((void*)Cache_Destruct(a))
513 static Cache* Cache_Construct (Cache *cache, int size, CacheDataFreeProc fproc);
514 static Cache* Cache_Destruct (Cache *cache);
515 static int   Cache_Insert (Cache *cache, long id, void *data);
516 static int   Cache_Delete (Cache *cache, long id);
517 static int   Cache_Touch (Cache *cache, long id);
518 static void* Cache_Lock (Cache *cache, long id);
519 static int   Cache_Unlock (Cache *cache, long id);
520 static void* Cache_Peek (Cache *cache, long id);
521 static void  Cache_Purge (Cache*);
522 static void  Cache_LogStats (Cache *cache, const char *name);
523 static char* Cache_ReportStats (Cache *cache, char *buffer);
524 static int   Cache_IsValid (Cache *cache);
525 
526 
527 /* ========== Misc. structs ========== */
528 
529 struct CdFInfo
530 {
531         char *ext;
532         unsigned short magic;
533         unsigned short format;
534 };
535 
536 struct CdDirInfo
537 {
538         char *key;
539         char *dir;
540         char *user_dir;
541         FILE **fd;
542 };
543 
544 typedef struct DivInfo
545 {
546         char tag[1+DIV_TAG_LEN];
547         short cd_num[TYPE_MAX];
548 }
549 DivInfo;
550 
551 /* ========== Misc. functions ========== */
552 
553 static FILE * CdEntrez_FileOpen (CdEntrezDir dirnum, int doctype, int divnum, int ftype, const char *fname, const char *fmode);
554 static void CdEntrez_FileClose (CdEntrezDir dirnum, int doctype, FILE *fd);
555 
556 static int InvalidConfiguration (int code);
557 static int FileCorrupt (const char *fname);
558 static int FileOutOfDate (const char *fname);
559 static int FileNotRecognized (const char *fname);
560 static int CatastrophicFailure (int code);
561 
562 static Boolean IsInitialized (void);
563 static Boolean ValidType (int type);
564 static Boolean ValidField (int field);
565 
566 static char * _GetAppParamStr (const char *filebase, const char *section, 
567                                 const char *key, const char *dlft);
568                                 
569 #ifndef GetAppParamStr
570 #define GetAppParamStr _GetAppParamStr
571 #endif
572 
573 static int LIBCALLBACK default_CdInsertProc (const char *volname, const CdDevInfo *dev);
574 static int LIBCALLBACK default_CdEjectProc (const char *volname, const CdDevInfo *dev);
575 
576 
577 /* ========== Static Variables ========== */
578 
579 static CdDevHook _hookInsert = default_CdInsertProc;
580 static CdDevHook _hookEject = default_CdEjectProc;
581 
582 static char * _empty_string = "";
583 
584 static HuffTable * _huff_tab[TYPE_MAX];
585 
586 static FILE* _fd_idx[TYPE_MAX];
587 static FILE* _fd_sum[TYPE_MAX];
588 static FILE* _fd_lnk[TYPE_MAX];
589 
590 #define HCA 1
591 #define SUM 2
592 #define LNK 3
593 #define OFS 4
594 #define OIX 5
595 #define TRM 6
596 #define TIX 7
597 #define PST 8
598 #define INF 9
599 
600 struct CdFInfo _finfo[] = {
601         "", 0, 0,
602         "hca", MAGIC_hca, FORMAT_hca,
603         "sum", MAGIC_sum, FORMAT_sum,
604         "lnk", MAGIC_lnk, FORMAT_lnk,
605         "ofs", MAGIC_ofs, FORMAT_ofs,
606         "oix", MAGIC_oix, FORMAT_oix,
607         "trm", MAGIC_trm, FORMAT_trm,
608         "tix", MAGIC_tix, FORMAT_tix,
609         "pst", MAGIC_pst, FORMAT_pst,
610         "inf", MAGIC_inf, FORMAT_inf
611 };
612 
613 
614 struct CdDirInfo _dir[] = {
615         SYS_KEYNAME, SYS_DIRNAME, NULL, NULL,
616         IDX_KEYNAME, IDX_DIRNAME, NULL, _fd_idx,
617         SUM_KEYNAME, SUM_DIRNAME, NULL, _fd_sum,
618         TRM_KEYNAME, TRM_DIRNAME, NULL, NULL,
619         LNK_KEYNAME, LNK_DIRNAME, NULL, _fd_lnk,
620         REC_KEYNAME, REC_DIRNAME, NULL, NULL    
621 };
622 
623 
624 static EntrezInfo * _cdinfo;
625 static short _init_ct;
626 static short _rel_major;
627 static short _rel_minor;
628 static short _device_ct;
629 static short _volume_ct;
630 static short _type_ct;
631 static short _div_ct;
632 static short _fld_ct;
633 static short _verbose_trace;
634 
635 static CdVolume * _cdvol; 
636 static CdDevice * _cddev;
637 
638 static DivInfo * _div;
639 static UidIndex * _uidx;
640 static TrmIndex ** _tidx[TYPE_MAX];
641 
642 #define IDX_REC_SIZE   20
643 
644 static int _idx_page_size;
645 static int _idx_page_slots;
646 static int _trm_page_size;
647 
648 static Cache * _idx_cache;
649 static Cache * _trm_cache;
650 
651 static char * _rcfile = "ncbi";
652 
653 static CdMap _map[CdDir_LAST+1];
654 
655 
656 
657 static int FileReadSwapShort (unsigned short *buffer, int count, FILE *fd);
658 static int FileReadSwapLong (unsigned long *buffer, int count, FILE *fd);
659 static int FileReadSwapInt4 (Uint4 *buffer, int count, FILE *fd);
660 
661 
662 
663 static char * FileReadStr (FILE *fd, int lbyte);
664 
665 
666 
667 
668 /****************************************************************************
669 *
670 *               EXPORTED APIs
671 *
672 *****************************************************************************/
673 
674 NLM_EXTERN Boolean  cd3_CdInit (void)
675 {
676         int n, j, k, pages;
677         char buffer[256];
678         unsigned short m[8];
679         CdRomInfo info;
680         CdDevice *dev;
681         AsnIo *aio;
682         FILE *fd;
683         CdDevice *dev_startup = NULL; 
684         
685 
686         _verbose_trace = GetAppParamBoolean(_rcfile,"CdEntrez","LogVerbose",FALSE);
687         if (_verbose_trace)
688           ErrLogPrintf("CdInit()     [%s %s]\n", THIS_MODULE, REVISION_STR);
689 
690         /*----- If already initialized, just increment counter -----*/
691         if (_init_ct > 0)
692         {
693                 _init_ct++;
694                 return TRUE;
695         }
696         
697 
698         /***if (!_verbose_trace)
699         {
700                 ErrLogPrintf("      Add \"LogVerbose=YES\" below [CdEntrez] in your NCBI\n");
701                 ErrLogPrintf("      configuration file to log detailed information to this file.\n");
702         }***/
703 
704         /*----- Gather information from config file -----*/
705         _device_ct = (int) GetAppParamInt(_rcfile,"CdEntrez","DeviceCount",0);
706         if (_device_ct == 0)
707                         return InvalidConfiguration(1);
708         
709         for (n=CdDir_FIRST; n<=CdDir_LAST; ++n)
710         {
711                 if (FindPath(_rcfile,"CdEntrez.Paths",_dir[n].key,buffer,sizeof buffer))
712                         _dir[n].user_dir = StrSave(buffer);
713         }
714 
715         /*----- Initialize the list of devices -----*/
716         _cddev = (CdDevice*) MemNew(sizeof(CdDevice) * _device_ct);
717         for (n=0, dev=_cddev; n<_device_ct; ++n)
718         {
719                 if (!CdDevice_Construct(dev++,n+1))
720                         return FALSE;
721         }
722         
723         /*----- Now we need locate a copy of cdvolume.inf -----*/
724         memset((void*)&info,0,sizeof info);
725         buffer[0] = '\0';
726         if (_dir[CdDir_sys].user_dir !=NULL)
727         {
728                 /* use the cdvolume.inf file on the hard disk */
729                 strcpy(buffer,_dir[CdDir_sys].user_dir);
730                 FileBuildPath(buffer,NULL,"cdvolume.inf");
731         }
732         else
733         {
734                 /* first, look for a bound device */
735                 for (k=0, dev=_cddev; k<_device_ct; ++k, ++dev)
736                 {
737                         if (_cddev[k].bound_cd && CdDevice_Init(dev))
738                         {
739                                 dev_startup = dev;
740                                 CdDevice_FileBuildPath(dev,buffer,_dir[CdDir_sys].dir,"cdvolume.inf");
741                                 break;
742                         }
743                 }               
744 
745                 if (dev_startup == NULL)
746                 {               
747                         /* if we reach this point, there are no bound devices, so
748                                 we now have to look through all of the devices, hoping
749                                 that one of them will have an Entrez CD-ROM already
750                                 inserted */
751 
752 /*
753 #ifndef OS_DOS
754 */
755                         for (k=0, dev=_cddev; k<_device_ct; ++k, ++dev)
756                         {
757                                 if (CdDevice_Init(dev))
758                                 {
759                                         dev_startup = dev;
760                                         break;
761                                 }
762                         }
763 /*
764 #endif
765 */
766                         
767                         if (dev_startup == NULL)
768                         {   
769                                 /* Now were're going to have to ask the user
770                                         to insert one of the CD's */
771                                 
772                                 dev_startup = &_cddev[0];
773                                         
774                                 if (!(*_hookInsert)("Entrez1",&dev_startup->inf))
775                                         return FALSE;
776                                 if (!CdDevice_Init(dev_startup))
777                                 {
778                                         VERBOSE("CdDevice_Init() failed, line %d\n",__LINE__);
779                                         return FALSE;
780                                 }
781                         }
782                         
783                         CdDevice_FileBuildPath(dev_startup,buffer,_dir[CdDir_sys].dir,"cdvolume.inf");
784                 }
785         }
786 
787         /*----- OK, now we've found cdvolume.inf, let's read it -----*/
788         if ((fd = FileOpen(buffer,"rb")) ==NULL)
789                 return FALSE;
790         FileReadSwapShort(m,6,fd); 
791         if (m[0] != MAGIC_inf)
792                 return FileCorrupt(buffer);
793         if (m[1] > FORMAT_inf)
794                 ErrPostEx(SEV_INFO,0,0,"File %s format number greater than expected",buffer);
795         _rel_major = m[2];
796         _rel_minor = m[3];
797         _volume_ct = m[5];
798         FileClose(fd);
799         VERBOSE("File cdvolume.inf read OK. \n"); 
800         VERBOSE("Release %d.%d (on %d CDs)\n",_rel_major,_rel_minor,_volume_ct); 
801 
802         /*----- Locate & read the cdentrez.inf file -----*/
803         if (_dir[CdDir_sys].user_dir !=NULL)
804                 FileBuildPath(strcpy(buffer,_dir[CdDir_sys].user_dir),NULL,"cdentrez.inf");
805         else
806                 CdDevice_FileBuildPath(dev_startup,buffer,_dir[CdDir_sys].dir,"cdentrez.inf");
807         if ((aio = AsnIoOpen(buffer,"r")) ==NULL)
808                 return InvalidConfiguration(1);
809         _cdinfo = EntrezInfoAsnRead(aio,NULL);
810         _cdinfo->cd_count = _volume_ct;
811         AsnIoClose(aio);
812         if (_cdinfo == NULL)
813         {
814                 ErrPostEx(SEV_INFO,0,0,"EntrezInfoAsnRead failure");
815                 return FALSE;
816         }
817         
818         _type_ct = _cdinfo->type_count;
819         _fld_ct = _cdinfo->field_count;
820         _div_ct = _cdinfo->div_count;
821         _idx_page_size = _cdinfo->type_bucket_size;
822         _idx_page_slots = _idx_page_size / IDX_REC_SIZE;
823         _trm_page_size = _cdinfo->field_bucket_size;
824         VERBOSE("File cdentrez.inf read OK.  \n");
825 
826         /*----- initialize the list of volumes -----*/
827         _cdvol = (CdVolume*) MemNew(sizeof(CdVolume) * _volume_ct);
828         for (n=0; n<_volume_ct; ++n)
829         {
830                 CdVolume_Construct(&(_cdvol[n]),n+1);
831 #ifdef OS_MAC
832                 if (_cdvol[n].device == NULL)  
833                 {   /* the volume is not associated with any device, see if it's on desktop */
834                         char volname[16];
835                         CdRomInfo info;
836 
837                         sprintf(volname,"entrez%d",n+1);
838                         if (CdTestPath(volname,&info))
839                         {       /* the volume is indeed on the desktop, find a device to mount it on */
840                                 int i;
841                                 for (i=0; i<_device_ct; ++i)
842                                 {
843                                         if (_cddev[i].volume == NULL)
844                                         {
845                                                 if (CdVolume_Mount(&_cdvol[n],&_cddev[i],FALSE))
846                                                         break;
847                                         }
848                                 }
849                         }
850                 }
851 #endif
852         }
853 
854         /*----- If fewer devices than volumes, at least one must be ejectable -----*/
855         if (_device_ct < _volume_ct)
856         {
857                 for (n=0; n<_device_ct; ++n)
858                 {
859                         if (_cddev[n].is_ejectable)     
860                                 break;
861                 }
862                 if (n == _device_ct)
863                         return InvalidConfiguration(2);
864         }
865         
866         /*----- Initialize index and term caches -----*/
867         n = (int) GetAppParamInt(_rcfile,"CdEntrez","IdxCacheSize",DEFAULT_IdxCacheSize);
868         pages = (int) ( (long)n * (long)KBYTE / (long)_idx_page_size);
869         pages = MAX(1,MIN(pages,256));
870         _idx_cache = Cache_New(pages,DefCacheDataFreeProc);
871         _idx_cache->page_size = _idx_page_size;
872         n = (int) GetAppParamInt(_rcfile,"CdEntrez","TrmCacheSize",DEFAULT_TrmCacheSize);
873         pages = (int) ( ((long)n * (long)KBYTE) / (long)_trm_page_size);
874         pages = MAX(1,MIN(pages,256));
875         _trm_cache = Cache_New(pages,DefCacheDataFreeProc);
876         _trm_cache->page_size = _trm_page_size;
877 
878         /*----- Initialize the UidIndex & TrmIndex structures -----*/
879         _uidx = (UidIndex*) MemNew(sizeof(UidIndex) * _type_ct);
880         for (n=0; n<_type_ct; ++n)
881         {
882                 UidIndex_Construct(&(_uidx[n]),(DocType)n,&(_cdinfo->types[n]));
883                 
884                 _tidx[n] = (TrmIndex**) MemNew(sizeof(TrmIndex*) * _fld_ct);
885                 for (j=0; j<_fld_ct; ++j)
886                 {
887                         if (_cdinfo->types[n].fields[j].num_terms > 0)
888                                 _tidx[n][j] = TrmIndex_New(n,j,FLDTAG(j),&(_cdinfo->types[n].fields[j]));
889                 }
890         }
891         
892         /*----- Read the cdlayout.inf file -----*/
893         if (_dir[CdDir_sys].user_dir !=NULL)
894                 FileBuildPath(strcpy(buffer,_dir[CdDir_sys].user_dir),NULL,"cdlayout.inf");
895         else
896                 CdDevice_FileBuildPath(dev_startup,buffer,_dir[CdDir_sys].dir,"cdlayout.inf");
897         if ((fd = FileOpen(buffer,"r")) ==NULL)
898                 return FALSE;
899         ReadCdLayout(fd);
900         FileClose(fd);
901         VERBOSE("File cdlayout.inf read OK \n");
902         
903         /*----- Done. return success -----*/
904         _init_ct++;
905         return TRUE;
906 }
907 
908 
909 NLM_EXTERN Boolean cd3_CdFini (void)
910 {
911         int i, j;
912         CdEntrezDir dir;
913         
914         _verbose_trace = GetAppParamBoolean(_rcfile,"CdEntrez","LogVerbose",FALSE);
915         if (_verbose_trace)     
916           ErrLogPrintf("CdFini()\n");
917 
918         if (!IsInitialized())
919                 return FALSE;           
920         if ((--_init_ct) > 0)
921                 return TRUE;
922         
923         /*----- Free index and cache data -----*/
924         if (Cache_IsValid(_idx_cache))
925         {
926                 Cache_LogStats(_idx_cache,"idx");
927                 Cache_Free(_idx_cache);
928         }
929         if (Cache_IsValid(_trm_cache))
930         {
931                 Cache_LogStats(_trm_cache,"trm");
932                 Cache_Free(_trm_cache); 
933         }
934         for (i=0; i<_type_ct; ++i)
935         {
936                 UidIndex_Destruct(&_uidx[i]);
937                 for (j=0; j<_fld_ct; ++j)
938                         TrmIndex_Free(_tidx[i][j]);
939                 MemFree((void*)_tidx[i]);
940                 _tidx[i] = NULL;
941                 HuffTable_Destruct(_huff_tab[i]);
942                 _huff_tab[i] = NULL;
943         }
944         MemFree((void*)_uidx);
945         
946         /*----- Free CdVolume and CdDevice data -----*/
947         for (i=0; i<_volume_ct; ++i)
948                 CdVolume_Destruct(&_cdvol[i]);
949         for (i=0; i<_device_ct; ++i)
950                 CdDevice_Destruct(&_cddev[i]); 
951         MemFree((void*)_cdvol);
952         MemFree((void*)_cddev);
953         
954         /*----- Free cdlayout stuff -----*/
955         for (dir=CdDir_FIRST; dir<=CdDir_LAST; ++dir)
956         {
957                 CdMap_Destruct(&_map[dir]);
958                 MemFree(_dir[dir].user_dir);
959         }
960 
961         /*----- Free the EntrezInfo struct -----*/
962         EntrezInfoFree(_cdinfo);
963         _cdinfo = NULL;
964         return TRUE;
965 }
966 
967 
968 NLM_EXTERN AsnIo* cd3_EntrezInfoOpen (const char *dirname)
969 {
970         VERBOSE("EntrezInfoOpen(%s)\n", dirname ? dirname : "NULL");
971         
972         /* NOT IMPLEMENTED */
973         
974         /* Do we need this?  Things seem to be working fine without it! */
975         
976         return NULL;
977 }
978 
979 
980 NLM_EXTERN EntrezInfo* cd3_CdGetInfo (void)
981 {
982         VERBOSE("CdGetInfo()\n");
983         
984         return _cdinfo;
985 }
986 
987 
988 NLM_EXTERN char* cd3_CdDetailedInfo (void)
989 {
990         char *detailed;
991         
992         VERBOSE("CdDetailedInfo()\n");
993 
994         if ((detailed  = (char*)Malloc(8192)) == NULL)
995                 return NULL;
996                 
997         if (!IsInitialized())
998         {
999                 strcpy(detailed,"*** NOT INITIALIZED ***");
1000         }
1001         else
1002         {
1003                 char *p = detailed;
1004                 sprintf(p,"Entrez release %d.%d\n",_rel_major,_rel_minor);
1005                 
1006                 if (_cdinfo->div_info != NULL)
1007                 {
1008                         EntrezDivInfo *div;
1009                         int i, j;
1010 
1011                         sprintf(p=strchr(p,0),"\nDIVISION INFORMATION\n");
1012 
1013                         for (i=0, div=_cdinfo->div_info; i<_cdinfo->div_count; ++i, ++div)
1014                         {
1015                                 sprintf(p=strchr(p,0),"   %s:  ",div->tag);
1016                                 sprintf(p=strchr(p,0),"%-36s  ",div->descr);
1017                                 sprintf(p=strchr(p,0),"%-25s\n",div->reldate ? div->reldate : "?");
1018                         }
1019 
1020                         sprintf(p=strchr(p,0),"\n   div        Document Counts by Type\n");
1021                         sprintf(p=strchr(p,0),"   tag         ml      aa      nt      st\n");
1022                         sprintf(p=strchr(p,0),"   ---     ------  ------  ------  ------\n");
1023                         for (i=0, div=_cdinfo->div_info; i<_cdinfo->div_count; ++i, ++div)
1024                         {
1025                                 sprintf(p=strchr(p,0),"   %s:   ",div->tag);
1026                                 if (div->docs != NULL)
1027                                 {
1028                                         for (j=0; j<_type_ct; ++j)
1029                                                 sprintf(p=strchr(p,0),"%7ld ",div->docs[j]);
1030                                 }
1031                                 sprintf(p=strchr(p,0),"\n");
1032                         }
1033                 }
1034                 
1035                 if (_idx_cache != NULL && _trm_cache != NULL)
1036                 {
1037                         sprintf(p=strchr(p,0),"\nCACHE STATISTICS\n");
1038                         sprintf(p=strchr(p,0),"\nIndex page cache:\n");                 
1039                         Cache_ReportStats(_idx_cache,strchr(p,0));
1040                         sprintf(p=strchr(p,0),"\nTerm page cache:\n");                  
1041                         Cache_ReportStats(_trm_cache,strchr(p,0));
1042                 }
1043         }               
1044         return detailed;
1045 }
1046 
1047 NLM_EXTERN int  cd3_CdTrmPageCt (DocType type, DocField fld)
1048 {
1049         VERBOSE("CdTrmPageCt(%d,%d)\n",type,fld);
1050         
1051         if (IsInitialized() && ValidType(type) && ValidField(fld))
1052         {
1053                 return _cdinfo->types[type].fields[fld].num_bucket; 
1054         }
1055         return 0;
1056 }
1057 
1058 NLM_EXTERN int  cd3_CdTrmLookup (DocType type, DocField fld, const char *term)
1059 {
1060         VERBOSE("CdTrmLookup(%d,%d,%s)\n",type,fld,term);
1061 
1062         if (IsInitialized() && ValidType(type) && ValidField(fld))
1063         {
1064                 TrmIndex *tidx = _tidx[type][fld];
1065                 if (tidx != NULL)
1066                         return TrmIndex_Lookup(tidx,term);
1067         }
1068         return -1;
1069 }
1070 
1071 NLM_EXTERN CdTerm* cd3_CdTrmFind (DocType type, DocField fld, const char *term)
1072 {
1073         CdTerm *trm = NULL;
1074         
1075         VERBOSE("CdTrmFind(%d,%d,%s)\n",type,fld,term);
1076         
1077         if (IsInitialized() && ValidType(type) && ValidField(fld))
1078         {
1079                 TrmIndex *tidx = _tidx[type][fld];
1080                 if (tidx != NULL)
1081                         trm = TrmIndex_GetCdTerm(tidx,term);
1082         }
1083         return trm;
1084 }
1085 
1086 NLM_EXTERN int cd3_CdTermScan (DocType type, DocField fld, int page_start, 
1087                         int page_count, CdTermProc proc)
1088 {
1089         int count=0;
1090         
1091         VERBOSE("CdTermScan(%d,%d,%d,%d,[proc])\n",type,fld,page_start,page_count);
1092         
1093         if (IsInitialized() && ValidType(type) && ValidField(fld))
1094         {
1095                 TrmIndex *tidx = _tidx[type][fld];
1096                 if (tidx != NULL)
1097                 {
1098                         if (page_count <=0)
1099                                 page_count = INT_MAX;
1100                         count = TrmIndex_ScanPages(tidx,page_start,page_count,proc);
1101                 }
1102         }
1103         return (Int2)count;
1104 }
1105 
1106 NLM_EXTERN long cd3_CdTrmUidsFil (DocType type, DocField fld, long offset,
1107                                                 long count, const char *filename, Boolean append)
1108 {
1109         long ct =0;
1110         
1111         VERBOSE("CdTrmUidsFil(%d,%d,%ld,%ld,%s,%s)\n",type,fld,offset,count,
1112                                         filename, append ? "TRUE" : "FALSE" );
1113         
1114         if (IsInitialized() && ValidType(type) && ValidField(fld))
1115         {
1116                 TrmIndex *tidx = _tidx[type][fld];
1117                 if (tidx != NULL)
1118                 {
1119                         FILE *fd1 = TrmIndex_PostingsFileOpen(tidx);
1120                         FILE *fd2 = FileOpen(filename,(append) ? "ab":"wb");
1121                         if (fd1 != NULL && fd2 != NULL)
1122                         {
1123                                 Uint4 arr[64];
1124                                 long m = DIM(arr);
1125                                 long n1, n2;
1126 
1127                                 VERIFY(fseek(fd1,offset,SEEK_SET) ==0);
1128 
1129                                 for (n1=count; n1>0; n1-=n2)
1130                                 {
1131                                         n2 = MIN(n1,m);
1132                                         n2 = FileReadSwapInt4(arr,(int)n2,fd1);
1133                                         FileWrite((void*)arr,sizeof(Int4),(size_t)n2,fd2);
1134                                 }
1135                                 ct = count - n1;
1136                         }
1137                         else VERBOSE("   * TrmIndex_PostingsFileOpen failed\n");
1138                         FileClose(fd1);
1139                         FileClose(fd2);
1140                 }
1141         }
1142         return ct;
1143 }
1144 
1145 
1146 NLM_EXTERN long cd3_CdTrmUidsMem (DocType type, DocField fld, long offset,
1147                                                                 long count, DocUid *mem)
1148 {
1149         VERBOSE("CdTrmUidsMem(%d,%d,%ld,%ld,[mem])\n",type,fld,
1150                                         offset,count);
1151         
1152         ASSERT(count < (long)(UINT_MAX/sizeof(long)));
1153         
1154         if (IsInitialized() && ValidType(type) && ValidField(fld))
1155         {
1156                 TrmIndex *tidx = _tidx[type][fld];
1157                 if (tidx != NULL)
1158                 {
1159                         FILE *fd = TrmIndex_PostingsFileOpen(tidx);
1160                         if (fd != NULL)
1161                         {
1162                                 VERIFY(fseek(fd,offset,SEEK_SET) ==0);
1163                                 count = FileReadSwapInt4((Uint4*)mem,(int)count,fd);
1164                                 FileClose(fd);
1165                                 return count;
1166                         }
1167                 }
1168         }
1169         return 0;
1170 }
1171 
1172 
1173 NLM_EXTERN int cd3_CdLinkUidGet (LinkSetPtr *result, DocType type,
1174                 DocType link_to_type, int numuid, DocUid *uid_list, 
1175                 Boolean mark_missing, long maxlink)
1176 {
1177         int i, cnt;
1178         DocUid uid, *puid = uid_list;
1179         UidIndex *uidx;
1180         UidIndexRec rec;
1181         FILE *fd=NULL;
1182         LSet *lset = NULL;
1183         
1184         VERBOSE("CdLinkUidGet([result],%d,%d,%d,[list],%s,%ld)\n",
1185                                 type,link_to_type,numuid,
1186                                 mark_missing ? "TRUE" : "FALSE", maxlink);
1187         
1188         if (! (IsInitialized() && ValidType(type) && ValidType(link_to_type)) )
1189         {
1190                 *result = NULL;
1191                 return 0;
1192         }
1193                 
1194         uidx = &(_uidx[type]);
1195         
1196         for (i=cnt=0; i<numuid; ++i, ++puid)
1197         {
1198                 uid = ABS(*puid);
1199                 if (UidIndex_Lookup(uidx,uid,&rec))
1200                 {
1201                         cnt++;
1202 
1203                         if (rec.lnk_offset > 0)
1204                         {               
1205                                 if (fd == NULL)
1206                                 {
1207                                         char fname[16];
1208                                         strcpy(fname,TYPTAG(type));
1209                                         strcat(fname,".lnk");
1210                                         if ((fd = CdEntrez_FileOpen(CdDir_lnk,type,rec.divnum,LNK,fname,"rb")) ==NULL)
1211                                                 break;
1212                                         maxlink = MIN(maxlink,INT_MAX);
1213                                         if ((lset = LSet_New(type,link_to_type,(int)maxlink)) ==NULL)
1214                                                 break;
1215                                 }
1216                                 if (fseek(fd,rec.lnk_offset,SEEK_SET) !=0)
1217                                 {
1218                                         ErrPostEx(SEV_INFO,0,0,"fseek failure");
1219                                         break;
1220                                 }
1221                                 if (!LSet_Read(lset,fd))
1222                                         break;
1223                         }
1224                 }
1225                 else if (mark_missing)
1226                 {
1227                         *puid = -uid;
1228                 }
1229         }
1230         
1231         CdEntrez_FileClose(CdDir_lnk,type,fd);
1232         *result = (lset==NULL) ? NULL : LSet_Convert(lset);
1233         return cnt;
1234 }
1235 
1236 #define BIT_non_document   0x001
1237 #define BIT_no_abstract    0x002
1238 #define BIT_no_authors     0x004
1239 #define BIT_trans_title    0x008
1240 #define BIT_is_partial     0x010
1241 #define BIT_is_segmented   0x020
1242 
1243 NLM_EXTERN DocSum* cd3_CdGetDocSum (DocType type, DocUid uid)
1244 {
1245         DocSum *sum = NULL;
1246         UidIndex *uidx;
1247         UidIndexRec rec;                            
1248         
1249         VERBOSE("CdGetDocSum(%d,%ld)\n",type,uid);
1250                 
1251         if (!IsInitialized() || !ValidType(type))
1252                 return NULL;            
1253         
1254         uidx = &(_uidx[type]);
1255         if (UidIndex_Lookup(uidx,uid,&rec) && rec.sum_offset > 0)
1256         {
1257                 char fname[16];
1258                 FILE *fd;
1259 
1260                 strcpy(fname,TYPTAG(type));
1261                 strcat(fname,".sum");
1262                 if ((fd = CdEntrez_FileOpen(CdDir_sum,type,0,SUM,fname,"rb")) !=NULL)
1263                 {
1264                         if (fseek(fd,rec.sum_offset,SEEK_SET) !=0)
1265                                 ErrPostEx(SEV_INFO,0,0,"fseek failure");
1266 
1267                         if ((sum = (DocSum*)MemNew(sizeof(DocSum))) !=NULL)
1268                         {
1269                                 unsigned short m[4];
1270                                 FileReadSwapShort(m,3,fd);
1271                                 
1272                                 /* m[0] is doc-flags */
1273                                 if (m[0] & BIT_non_document)  sum->non_document = TRUE;
1274                                 if (m[0] & BIT_no_abstract)  sum->no_abstract = TRUE;
1275                                 if (m[0] & BIT_no_authors)  sum->no_authors = TRUE;
1276                                 if (m[0] & BIT_trans_title)  sum->translated_title = TRUE;
1277                                 if (m[0] & BIT_is_partial)  sum->is_partial = TRUE;
1278                                 if (m[0] & BIT_is_segmented)  sum->is_segmented = TRUE;
1279 
1280                                 /* m[1] is create date */
1281                                 if (m[1] != 0)
1282                                 {
1283                                         sum->create.year =  1950 + ((int) (m[1] & 0xFE00) >> 9);
1284                                         sum->create.month = ((int) (m[1] & 0x01E0) >> 5);
1285                                         sum->create.day = (m[1] & 0x001F);
1286                                 }
1287                                 
1288                                 /* m[2] is modify date */
1289                                 if (m[2] != 0)
1290                                 {
1291                                         sum->modify.year =  1950 + ((int) (m[2] & 0xFE00) >> 9);
1292                                         sum->modify.month = ((int) (m[2] & 0x01E0) >> 5);
1293                                         sum->modify.day = (m[2] & 0x001F);
1294                                 }
1295                                 
1296                                 FileReadSwapShort((unsigned short*)sum->link_count,_type_ct,fd);
1297                                 sum->caption = FileReadStr(fd,1);
1298                                 sum->title = FileReadStr(fd,2);
1299                                 sum->extra = FileReadStr(fd,1);
1300                                 sum->uid = uid;
1301                         }
1302                         CdEntrez_FileClose(CdDir_sum,type,fd);
1303                 }
1304         }
1305         else
1306         {
1307                 VERBOSE("   * bad UID %ld ?\n", uid);
1308         }
1309         return sum;
1310 }
1311 
1312 NLM_EXTERN AsnIo* cd3_CdDocAsnOpen (DocType type, DocUid uid)
1313 {
1314         char fname[16];
1315         AsnIo *aio = NULL;
1316         UidIndex *uidx;
1317         UidIndexRec rec;
1318         
1319         VERBOSE("CdDocAsnOpen(%d,%ld)\n",type,uid);
1320                 
1321         if (!IsInitialized() || !ValidType(type))
1322                 return NULL;
1323 
1324         uidx = &(_uidx[type]);
1325         if (UidIndex_Lookup(uidx,uid,&rec))
1326         {
1327                 char *fdir = NULL;
1328                 FILE *fd;
1329                 DecompInfo *decomp;
1330                 int c;
1331 
1332                 sprintf(fname,"%s%s%03d.hca",TYPTAG(type),DIVTAG(rec.divnum-1),rec.filnum);
1333                 if ((fd = CdEntrez_FileOpen(CdDir_rec,uidx->type,rec.divnum,HCA,fname,"rb")) ==NULL)
1334                         return NULL;
1335 
1336                 if (_huff_tab[type] == NULL)
1337                 {
1338                         fseek(fd,22,SEEK_SET);
1339                         _huff_tab[type] = HuffTable_Read(fd);
1340                         if (_huff_tab[type] == NULL)
1341                         {
1342                                 return (AsnIo*) CatastrophicFailure(1);
1343                         }
1344                 }
1345                 if (fseek(fd,rec.hca_offset,SEEK_SET) !=0)
1346                 {
1347                         FileClose(fd);
1348                         ErrPostEx(SEV_INFO,0,0,"fseek failure");
1349                 }
1350                 else
1351                 {
1352                         c = fgetc(fd);
1353                         if ((c & 0x0F) != 1)
1354                                 ErrPostEx(SEV_INFO,0,0,"unknown error");
1355                         c = fgetc(fd);
1356                         c = fgetc(fd);
1357                         c = fgetc(fd);
1358                         decomp = DecompInfo_New(_huff_tab[type]);
1359                         aio = DecompInfo_Attach(decomp,fd);
1360                 }
1361         }
1362         return aio;
1363 }
1364 
1365 NLM_EXTERN AsnIo* cd3_CdDocAsnClose (AsnIo* aio)
1366 {
1367         DecompInfo *info;
1368         FILE *fd;
1369         
1370         VERBOSE("CdDocAsnClose([aio])\n");
1371         
1372         ASSERT(aio != NULL);
1373         info = (DecompInfo*) aio->iostruct;
1374         ASSERT(info!= NULL);
1375         fd = DecompInfo_Detach(info);
1376         FileClose(fd);
1377         DecompInfo_Free(info);
1378         return NULL;
1379 }
1380 
1381 
1382 Boolean LIBCALL CdTestPath (const char *path, CdRomInfo *info)
1383 {
1384         static char *fdir = SYS_DIRNAME;
1385         static char *fname = "cdvolume.inf";
1386         char fpath[256], *p;
1387         unsigned short m[8];
1388         FILE *fd;
1389         
1390         VERBOSE("CdTestPath(%s,[info])\n",path);
1391         ASSERT(info != NULL);
1392         
1393         memset((void*)info,0,sizeof(CdRomInfo));
1394         
1395         /*----- lowercase, without ";1" -----*/
1396         strcpy(fpath,path);
1397         p = strchr(fpath,'\0');
1398         FileBuildPath(fpath,fdir,fname);
1399         StrLower(p);
1400         if (FileLength(fpath) >0)
1401                 goto DisneyLand;
1402         VERBOSE("   access attempt [%s] failed\n",fpath);
1403 
1404         /*----- lowercase, with ";1" -----*/
1405         strcat(fpath,";1");
1406         if (FileLength(fpath) >0)
1407         {
1408                 info->semicolon_one = 1;
1409                 goto DisneyLand;
1410         }
1411         VERBOSE("   access attempt [%s] failed\n",fpath);
1412 
1413         /*----- uppercase, without ";1" -----*/
1414         info->upper_case = 1;
1415         strcpy(fpath,path);
1416         FileBuildPath(fpath,fdir,(char*)fname);
1417         StrUpper(p);
1418         if (FileLength(fpath) >0)
1419                 goto DisneyLand;
1420         VERBOSE("   access attempt [%s] failed\n",fpath);
1421 
1422         /*----- uppercase, with ";1" -----*/
1423         strcat(fpath,";1");
1424         if (FileLength(fpath) >0)
1425         {
1426                 info->semicolon_one = 1;
1427                 goto DisneyLand;
1428         }
1429 
1430         VERBOSE("   access attempt [%s] failed\n",fpath);
1431         VERBOSE("   struck out on [%s].  must be either invalid or not mounted\n",path);
1432         return FALSE;
1433 
1434         
1435 /*----- success -----*/
1436  DisneyLand:       
1437         VERBOSE("   access attempt [%s] succeeded\n",fpath);
1438         
1439         if ((fd = FileOpen(fpath,"rb")) == NULL)
1440         {
1441                 /*ErrPostEx(SEV_ERROR,2,3,"fopen(\"%s\",\"rb\") failed\n",fpath);*/
1442                 return FALSE;
1443         }
1444 
1445         /*----- read first 5 values in file -----
1446         *       m[0]  magic value for this file
1447         *       m[1]  format code for this file
1448         *       m[2]  cdrom release number (major portion)
1449         *       m[3]  cdrom release number (minor portion)
1450         *       m[4]  cdrom volume number (1, 2, or 3)
1451         *       m[5]  number of CD-ROMs in the set
1452         */
1453                 
1454         FileReadSwapShort(m,6,fd);   /**** need to check for error ****/
1455         FileClose(fd);
1456         if (m[0] != MAGIC_inf)
1457                 return FileCorrupt(fpath);
1458         if (m[1] != FORMAT_inf)
1459                 VERBOSE("   unexpected format number (%d) for file %s\n",(int)m[1],fpath);
1460         info->rel_major = m[2];
1461         info->rel_minor = m[3];
1462         info->cd_num = m[4];
1463         info->cd_count = m[5];
1464         VERBOSE("   release %d.%d; cd %d of %d\n",(int)m[2],(int)m[3],(int)m[4],(int)m[5]);
1465         return TRUE;    
1466 }
1467 
1468 
1469 static char UnixReadChar (FILE *f)
1470 
1471 {
1472   char  ch;
1473   int   getcrsult;
1474 
1475   ch = '\0';
1476   if (f != NULL) {
1477     getcrsult = fgetc (f);
1478     ch = (char) getcrsult;
1479     if (getcrsult == EOF && feof (f)) {
1480       ch = '\0';
1481     }
1482   }
1483   return ch;
1484 }
1485 
1486 static char *UnixFileGets(char *buff, int maxsize, FILE *f)
1487 
1488 {
1489   int  ch;
1490   int  i;
1491 
1492   if (buff != NULL && maxsize > 0) {
1493     *buff = '\0';
1494     if (f != NULL) {
1495       i = 0;
1496       ch = UnixReadChar (f);
1497       while (ch != '\0' && ch != '\r' && ch != '\n' && i < maxsize) {
1498         buff [i] = ch;
1499         i++;
1500         ch = UnixReadChar (f);
1501       }
1502       if (ch != '\0') {
1503         buff [i] = ch;
1504         i++;
1505       }
1506       buff [i] = '\0';
1507     }
1508   }
1509   if (ch == '\0') {
1510     return NULL;
1511   } else {
1512     return buff;
1513   }
1514 }
1515 
1516 NLM_EXTERN long LIBCALL cd3_CdEnumFiles (CdEntrezDir dir, DocType type, const char *div,
1517                         EntrezEnumFileProc proc, void *opaque_data)
1518 {
1519         FILE *fd;
1520         
1521         VERBOSE("CdEnumFiles(%d,%d,%s,[proc],[opaque])\n",dir,type,
1522                                 div ? div : "NULL");
1523         
1524         if ((fd = CdEntrez_FileOpen(CdDir_sys,0,0,0,"cdlayout.inf","r")) != NULL)
1525         {
1526                 char prefix[32], *p;
1527                 char line[80];  
1528                 long size, total_size = 0;
1529                 int doit = FALSE;
1530                 int count =0;
1531                 int cdnum;
1532                         
1533                 strcpy(prefix,_dir[dir].key);
1534                 if (type >= 0)
1535                 {
1536                         strcat(prefix,"-");
1537                         strcat(prefix,TYPTAG(type));
1538                         if (div != NULL)
1539                         {
1540                                 strcat(prefix,"-");
1541                                 strcat(prefix,div);
1542                         }
1543                 }
1544                 
1545                 while (UnixFileGets(line,sizeof line,fd))
1546                 {
1547                         if (strchr("#\r\n",line[0])) 
1548                                 continue;  /* comment line */
1549                                 
1550                         if (isalpha(line[0]))
1551                         {
1552                                 if (doit)
1553                                         break;
1554                                         
1555                                 if (strncmp(line,prefix,strlen(prefix))==0)
1556                                 {
1557                                         doit = TRUE;
1558                                         VERIFY(p=strchr(line+1,'\t'));
1559                                         *p++ = '\0';
1560                                         cdnum = atoi(p);
1561                                 }
1562                                 else doit = FALSE;
1563                         }
1564                         else if (line[0] == '\t')
1565                         {
1566                                 if (doit)
1567                                 {
1568                                         VERIFY(p=strchr(line+1,'\t'));
1569                                         *p++ = '\0';
1570                                         size = atol(p);
1571                                         total_size += size;
1572                                         count++;
1573                                         if (proc != NULL)
1574                                         {
1575                                                 if (!(*proc)(cdnum,_dir[dir].dir,line+1,size,opaque_data))
1576                                                         break;
1577                                         }
1578                                 }
1579                         }
1580                 }
1581                 FileClose(fd);
1582 
1583                 if (count==0 && dir==CdDir_sys)
1584                 {
1585                         static char * fn[] = { "cdentrez.inf", "cdlayout.inf", "cdvolume.inf" };
1586                         static long   fs[] = { 9865, 4504, 12 };
1587                         int i;
1588                         
1589                         /* The list of filenames and filesizes for the sysinfo directory
1590                                 was inadvertently omitted from the cdlayout.inf file on the 
1591                                 sample CD-ROMs distributed to developers.  This section of
1592                                 code should never be executed in the final release. */
1593                         
1594                         VERBOSE("   HACK (faking cdlayout.inf information for SYSINFO directory)\n");
1595                         for (i=0; i<DIM(fs); ++i, ++count)
1596                         {
1597                                 if (proc != NULL)
1598                                 {
1599                                         if (!(*proc)(1,_dir[dir].dir,fn[i],fs[i],opaque_data))
1600                                                 break;
1601                                 }
1602                                 total_size += fs[i];
1603                         }
1604                 }
1605 
1606                 VERBOSE("   %d files enumerated\n",count);
1607                 return total_size;
1608         }
1609         return 0;
1610 }
1611 
1612 
1613 NLM_EXTERN CdDevHook LIBCALL CdSetInsertHook (CdDevHook hook)
1614 {
1615     CdDevHook prev = _hookInsert;
1616 
1617         VERBOSE("CdSetInsertHook\n");
1618 
1619         _hookInsert = (hook==NULL) ? hook : default_CdInsertProc;
1620         return prev;
1621 }
1622 
1623 
1624 NLM_EXTERN CdDevHook LIBCALL CdSetEjectHook (CdDevHook hook)
1625 {
1626     CdDevHook prev = _hookEject;
1627 
1628         VERBOSE("CdSetEjectHook\n");
1629 
1630         _hookEject = (hook==NULL) ? hook : default_CdEjectProc;
1631         return prev;
1632 }
1633 
1634 
1635 NLM_EXTERN Boolean LIBCALL CdMountEntrezVolume (int cdnum, char *root, size_t buflen)
1636 {
1637         if (root != NULL)  *root = '\0';
1638 
1639         if (cdnum < 1 || cdnum > _volume_ct)
1640                 ErrPostEx(SEV_WARNING,1,0,"Entrez CD number (%d) out of range",cdnum);
1641         else
1642         {
1643                 CdVolume *cdvol = & _cdvol[cdnum-1];
1644                 CdDevice *cddev;
1645 
1646                 if ((cddev = cdvol->device) ==NULL)
1647                         cddev = CdVolume_Mount(cdvol,NULL,TRUE);
1648 
1649                 if (cddev != NULL)
1650                 {
1651                         if (root != NULL)
1652                         {
1653                                 char temp[256];
1654                                 CdDevice_FileBuildPath(cddev,temp,NULL,NULL);
1655                                 strncat(root,temp,buflen);
1656                         }
1657                         return TRUE;
1658                 }
1659         }
1660         return FALSE;
1661 }
1662 
1663 
1664 NLM_EXTERN Boolean LIBCALL CdUnmountEntrezVolume (int cdnum)
1665 {
1666         CdVolume *cdvol;
1667         CdDevice *cddev;
1668 
1669         if (cdnum < 1 || cdnum > _volume_ct)
1670         {
1671                 ErrPostEx(SEV_WARNING,1,0,"Entrez CD number (%d) out of range",cdnum);
1672                 return FALSE;
1673         }
1674 
1675         cdvol = & _cdvol[cdnum-1];
1676         cddev = cdvol->device;
1677         if (cddev != NULL && cddev->is_ejectable)
1678         {
1679                 char volume_to_eject[32];
1680                 volume_to_eject[0] = '\0';
1681                 if (cddev->ins_volname)
1682                         sprintf(volume_to_eject,"entrez%d",cddev->volume->volume_num);
1683                 else if (cdvol->volume_name != NULL)
1684                         strcpy(volume_to_eject,cdvol->volume_name);
1685 
1686                 if (!(*_hookEject)(volume_to_eject,&cddev->inf))
1687                         ErrPostEx(SEV_INFO,0,0,"Unable to eject %s",cdvol->volume_name);
1688 
1689                 return CdVolume_Unmount(cdvol);
1690         }
1691         return FALSE;
1692 }
1693 
1694 
1695 /****************************************************************************
1696 *
1697 *               STATIC FUNCTIONS
1698 *
1699 *****************************************************************************/
1700 
1701 
1702 static Boolean IsInitialized (void)
1703 {
1704         if (_cdinfo == NULL)
1705         {
1706                 ErrPostEx(SEV_INFO,ERR_NotInited,1,"Library not initialized");
1707                 return FALSE;
1708         }
1709         return TRUE;
1710 }
1711 
1712 
1713 static Boolean ValidType (int type)
1714 {
1715         if (!TYPE_IS_VALID(type))
1716         {
1717                 ErrPostEx(SEV_INFO,ERR_BadParam,SUB_DocType,
1718                                         "DocType value (%d) out of range ",type);
1719                 return FALSE;
1720         }
1721         return TRUE;
1722 }
1723 
1724 
1725 static Boolean ValidField (int field)
1726 {
1727         if (!FIELD_IS_VALID(field))
1728         {
1729                 ErrPostEx(SEV_INFO,ERR_BadParam,SUB_DocField,
1730                                         "DocField value (%d) out of range ",field);
1731                 return FALSE;
1732         }
1733         return TRUE;
1734 }
1735 
1736 static FILE * CdEntrez_FileOpen (CdEntrezDir dirnum, int doctype, int divnum, 
1737                         int ftype, const char *fname, const char *fmode)
1738 {
1739         static int cdnum[CDVOL_MAX];
1740         FILE *fd = NULL;
1741         CdMap *map;
1742         int cdcnt;
1743         char fpath[256];
1744         
1745         ASSERT(dirnum >= CdDir_FIRST && dirnum <= CdDir_LAST);
1746         ASSERT(doctype < _type_ct);
1747         ASSERT(divnum <= _div_ct);
1748 
1749         fpath[0] = '\0';
1750         cdcnt = 0;
1751         map = &_map[dirnum];
1752         cdcnt = CdMap_GetSpecs(map,cdnum,fpath);
1753         if ((map = CdMap_GetChild(map,doctype)) != NULL)
1754         {
1755                 cdcnt = CdMap_GetSpecs(map,cdnum,fpath);
1756                 if ((map = CdMap_GetChild(map,divnum)) != NULL)
1757                 {
1758                         cdcnt = CdMap_GetSpecs(map,cdnum,fpath);
1759                 }
1760         }
1761         
1762         if (fpath[0] != '\0' && fpath[0] != '#')
1763         {
1764                 FileBuildPath(fpath,NULL,(char*)fname);
1765                 fd = FileOpen(fpath,fmode);
1766         }
1767         else if (cdcnt > 0)
1768         {
1769                 CdVolume *cdvol;
1770                 CdDevice *cddev;
1771                 int n;
1772                 
1773                 if (cdcnt == 1)
1774                 {
1775                         n = cdnum[0];
1776                         ASSERT(VOLNUM_IS_VALID(n));
1777                         cdvol = &_cdvol[n-1];
1778                 }
1779                 else
1780                 {
1781                         /* If the information sought exists on more than one volume, select
1782                                 a mounted volume if possible.  Favor most-recently-used volumes. */
1783                         int i;
1784                         CdVolume *cdv;
1785                         
1786                         for (i=0, cdvol=NULL; i<cdcnt; ++i)
1787                         {
1788                                 n = cdnum[i];
1789                                 ASSERT(VOLNUM_IS_VALID(n));
1790                                 cdv = & _cdvol[n-1];
1791                                 if (cdvol == NULL)
1792                                         cdvol = cdv;
1793                                 else if (!CdVolume_IsMounted(cdvol) && CdVolume_IsMounted(cdv))
1794                                         cdvol = cdv;
1795                                 else if (cdv->timestamp > cdvol->timestamp)
1796                                         cdvol = cdv;
1797                         }
1798                 }
1799                 ASSERT(cdvol != NULL);
1800                                         
1801                 if ((cddev=cdvol->device) ==NULL)
1802                 {
1803                         if ((cddev=CdVolume_Mount(cdvol,NULL,TRUE)) ==NULL)
1804                         {
1805                                 VERBOSE("   * CdVolume_Mount failed\n");
1806                                 return NULL;
1807                         }
1808                 }
1809                 fd = CdDevice_FileOpen(cddev,_dir[dirnum].dir,fname,fmode);     
1810         }
1811         
1812         if (fd != NULL)
1813         {
1814                 if (ftype == 0)
1815                         return fd;
1816                 else
1817                 {
1818                         unsigned short m[4];
1819                         
1820                         if (FileReadSwapShort(m,4,fd) != 4)
1821                         {
1822                                 FileClose(fd);
1823                                 FileCorrupt(fname);
1824                                 return NULL;
1825                         }
1826                         fseek(fd,0,SEEK_SET);
1827                         /**VERBOSE("HEADER %s:  %d %d %d %d\n",fname,m[0],m[1],m[2],m[3]);**/
1828                         
1829                         if (m[0] && m[0] != _finfo[ftype].magic)
1830                                 FileCorrupt(fname);
1831                         else if (m[1] && m[1] > _finfo[ftype].format)
1832                                 FileNotRecognized(fname);
1833                         else if (m[2] && m[2] != (unsigned short) _cdinfo->version)
1834                                 FileOutOfDate(fname);
1835                         else if (m[3] && m[3] != (unsigned short) _cdinfo->issue && ftype != HCA)
1836                                 FileOutOfDate(fname);
1837                         else
1838                                 return fd;
1839                 }
1840         }
1841         FileClose(fd);
1842         return NULL;
1843 }
1844 
1845 
1846 static void CdEntrez_FileClose (CdEntrezDir dirnum, int doctype, FILE *fd)
1847 {
1848 
1849         /************ NOT IMPLEMENTED *************/
1850         
1851     
1852     /*  This function will close some files and leave others
1853         open depending on whether the device on which they 
1854         reside is ejectable or not.   Right now it just closes
1855         everything. */
1856     
1857     
1858         FileClose(fd);
1859 }
1860 
1861 
1862 
1863 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
1864 /* UidIndex Functions.        Schuler 06-13-94                                 */
1865 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
1866 #define UIDX_KEY(a,b)   (((unsigned long)(a) << 24) | (b))
1867 
1868 
1869 static UidIndex* UidIndex_Construct (UidIndex *uidx, DocType type, EntrezTypeData *info)
1870 {
1871         if (uidx != NULL)
1872         {
1873                 uidx->type = type;
1874                 uidx->uid_ct = info->num_uids;
1875                 uidx->uid_min = info->minuid;
1876                 uidx->uid_max = info->maxuid;
1877                 uidx->pages = info->num_bucket;
1878                 uidx->index = NULL;
1879         }
1880         return uidx;
1881 }
1882 
1883 
1884 static UidIndex* UidIndex_Destruct (UidIndex *uidx)
1885 {
1886         if (uidx != NULL)
1887                 MemFree((void*)uidx->index);
1888 
1889         return uidx;
1890 }
1891 
1892 
1893 static int UidIndex_ReadPageMap (UidIndex *uidx)
1894 {
1895         char fname[32];
1896         long *index;
1897         FILE *fd;
1898         
1899         ASSERT(uidx != NULL);
1900         ASSERT(TYPE_IS_VALID(uidx->type));
1901         ASSERT(uidx->index == NULL);
1902         
1903         if ((index = (long*) MemNew(sizeof(long) * uidx->pages)) ==NULL)
1904                 return FALSE;
1905 
1906         sprintf(fname,"%s.oix",TYPTAG(uidx->type));
1907         if ((fd = CdEntrez_FileOpen(CdDir_idx,uidx->type,0,OIX,fname,"rb")) ==NULL)
1908                 return FALSE;
1909         fseek(fd,8,SEEK_SET); /* to skip over header */
1910         if (FileReadSwapLong((unsigned long*)index,uidx->pages,fd) != uidx->pages)
1911                 return CatastrophicFailure(1);
1912         FileClose(fd);
1913         uidx->index = index;
1914         return TRUE;
1915 }
1916 
1917 
1918 static long* UidIndex_ReadPage (UidIndex *uidx, int pagenum)
1919 {
1920         int recs, vals;
1921         size_t bytes;
1922         char fname[32];
1923         FILE *fd;
1924         long *page;
1925         
1926         ASSERT(uidx != NULL);
1927         ASSERT(pagenum < uidx->pages && pagenum >= 0);
1928         
1929         /*----- open file, check magic value, version, etc -----*/
1930         sprintf(fname,"%s.ofs",TYPTAG(uidx->type));
1931         if ((fd = CdEntrez_FileOpen(CdDir_idx,uidx->type,0,OFS,fname,"rb")) ==NULL)
1932                 return NULL;
1933         fseek(fd,8,SEEK_SET);  /* to skip over header */
1934         
1935         /*----- seek to the right position, allocate memory, read the page -----*/
1936         if (fseek(fd,(long)pagenum*_idx_page_size,SEEK_CUR) != 0)
1937                 return (long*)CatastrophicFailure(1);
1938         recs = (pagenum < uidx->pages-1) ? _idx_page_slots :
1939                                 (int)(uidx->uid_ct - (long)pagenum*_idx_page_slots);
1940         vals = 5 * recs;
1941         bytes = vals * sizeof(long);
1942         if ((page = MemGet(bytes,MGET_ERRPOST)) ==NULL)
1943                 return NULL;
1944         if (FileReadSwapLong((unsigned long*)page,vals,fd) != vals)
1945                 return (long*)CatastrophicFailure(1);
1946         FileClose(fd);
1947         return page;
1948 } 
1949 
1950 
1951 static int UidIndex_Lookup (UidIndex *uidx, DocUid uid, UidIndexRec *rec)
1952 {
1953         int lo, hi, mid;
1954         int page_num, page_slots;
1955         unsigned long cache_key;
1956         long *page, *p;
1957         Boolean missed =FALSE;
1958         
1959         ASSERT(rec != NULL);
1960         memset((void*)rec,0,sizeof(UidIndexRec));
1961         
1962         if (uid < uidx->uid_min || uid > uidx->uid_max)
1963                 return FALSE;
1964         
1965         /*----- load the index, if necessary -----*/
1966         if (uidx->index == NULL)
1967         {
1968                 if (!UidIndex_ReadPageMap(uidx))
1969                         return FALSE;
1970         }
1971 
1972         /*----- binary search the index -----*/
1973         lo = 0;
1974         hi = (int)uidx->pages -1;
1975         while (lo <= hi)
1976         {
1977                 mid = (lo + hi) /2;
1978                 p = uidx->index + mid;
1979                 if (*p == uid)
1980                         break;
1981                 if (uid < *p)
1982                         hi = mid-1;
1983                 else
1984                         lo = mid+1;
1985         }
1986         page_num = (uid < *p && mid >0) ? mid-1 : mid;
1987         page_slots = (page_num < uidx->pages -1) ? _idx_page_slots :
1988                         (int)(uidx->uid_ct - (long)page_num * _idx_page_slots);
1989 
1990         
1991         /*----- check to see if desired page is in cache -----*/
1992         cache_key = UIDX_KEY(uidx->type,page_num);
1993         page = (long*) Cache_Lock(_idx_cache,cache_key);
1994         if (page == NULL)
1995         {
1996                 missed = TRUE;
1997                 if ((page = UidIndex_ReadPage(uidx,page_num)) ==NULL)
1998                         return FALSE;
1999                 if (!Cache_Insert(_idx_cache,cache_key,page))
2000                         return CatastrophicFailure(1);
2001         }
2002         
2003         
2004         /*----- Binary search the page using the uid as a key -----*/
2005         lo = 0;
2006         hi = page_slots -1;
2007         
2008         while (lo <= hi)
2009         {
2010                 mid = (lo + hi) /2;
2011                 p = page + mid*5;
2012                 if (*p == uid)
2013                 {
2014                         rec->uid = *p++;
2015                         rec->divnum = (short) ((*p & 0x0FFF0000) >>16);
2016                         rec->filnum = (short) (*p++ & 0x0000FFFF);
2017                         rec->hca_offset = *p++; 
2018                         rec->sum_offset = *p++; 
2019                         rec->lnk_offset = *p;   
2020                         break;
2021                 }
2022                 if (uid < *p)
2023                         hi = mid-1;
2024                 else
2025                         lo = mid+1;
2026         }
2027         if (!missed)
2028                 Cache_Unlock(_idx_cache,cache_key);
2029         return (rec->uid != 0);
2030 }
2031 
2032 
2033 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
2034 /* TrmIndex Functions.        Schuler 07-02-94                                 */
2035 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
2036 #define TIDX_KEY(a,b,c)   (((unsigned long)(a) << 24) | ((unsigned long)(b) << 16) | (c))
2037 #define T_HEAD_SIZE  16
2038 
2039 
2040 static TrmIndex* TrmIndex_Construct (TrmIndex *tidx, int type, int fld, 
2041                                 const char *tag, EntrezFieldData *info)
2042 {
2043         if (tidx != NULL)
2044         {
2045                 tidx->type = type;
2046                 tidx->fld = fld;
2047                 strcpy(tidx->tag,tag);
2048                 tidx->trm_ct = info->num_terms;
2049                 tidx->page_ct = info->num_bucket;
2050         }
2051         return tidx;
2052 }
2053 
2054 
2055 static TrmIndex* TrmIndex_Destruct (TrmIndex *tidx)
2056 {
2057         if (tidx != NULL)
2058         {
2059                 char **p = tidx->index;
2060                 if (p != NULL)
2061                 {
2062                         int i;
2063                         for (i=0; i<tidx->page_ct; ++i, ++p)
2064                                 MemFree((void*)*p);
2065                         MemFree((void*)tidx->index);
2066                 }
2067                 tidx = NULL;
2068         }
2069         return tidx;
2070 }
2071 
2072 
2073 static int TrmIndex_ReadPageMap (TrmIndex *tidx)
2074 {
2075         char fname[32];
2076         unsigned short m[8];
2077         char **p;
2078         FILE *fd;
2079         int i;
2080         
2081         ASSERT(tidx != NULL);
2082         ASSERT(tidx->index == NULL);
2083         
2084         sprintf(fname,"%s%s.tix",TYPTAG(tidx->type),FLDTAG(tidx->fld));
2085         if ((fd = CdEntrez_FileOpen(CdDir_trm,tidx->type,0,TIX,fname,"rb")) ==NULL)
2086                 return FALSE;
2087         FileReadSwapShort(m,8,fd);
2088         tidx->page_size = (m[4]==0) ? _trm_page_size : m[4];
2089         if ((tidx->index = (char**) MemNew(sizeof(char*) * tidx->page_ct)) !=NULL)
2090         {
2091                 for (i=0, p=tidx->index; i<tidx->page_ct; ++i, ++p)
2092                 {
2093                         if ((*p = FileReadStr(fd,1)) ==NULL)
2094                         {
2095                                 TrmIndex_Destruct(tidx);
2096                                 break;
2097                         }
2098                 }
2099         }
2100         FileClose(fd);
2101         return (tidx->index != NULL);
2102 }
2103 
2104 
2105 static Byte* TrmIndex_ReadPage (TrmIndex *tidx, int pagenum)
2106 {
2107         Byte *page = NULL;
2108         char fname[32];
2109         long offset;
2110         FILE *fd;
2111         
2112         ASSERT(tidx != NULL);
2113         ASSERT(pagenum < tidx->page_ct);
2114 
2115         sprintf(fname,"%s%s.trm",TYPTAG(tidx->type),FLDTAG(tidx->fld));
2116         if ((fd = CdEntrez_FileOpen(CdDir_trm,tidx->type,0,TRM,fname,"rb")) ==NULL)
2117                 return FALSE;
2118         offset = (long)T_HEAD_SIZE + (long)tidx->page_size * pagenum;
2119         VERIFY(fseek(fd,offset,SEEK_SET) ==0);
2120                 
2121         page = MemGet(tidx->page_size+1, MGET_ERRPOST);
2122         if (page != NULL)
2123         {
2124                 size_t n = fread((void*)page,1,tidx->page_size,fd);  /*check for error*/
2125                 *(page+n) = '\0';   /* sentinel */
2126                 if (*page != 0xAB)
2127                         FileCorrupt(fname);
2128         }
2129         FileClose(fd);
2130         return page;
2131 }
2132 
2133 
2134 static int TrmIndex_Lookup (TrmIndex *tidx, const char *stem)
2135 {
2136         int lo, hi, mid, len, d;
2137         char *term;
2138         
2139         
2140         ASSERT(stem != NULL);
2141         len = strlen(stem);
2142 
2143         /*----- load the index, if necessary -----*/
2144         if (tidx->index == NULL)
2145         {
2146                 if (!TrmIndex_ReadPageMap(tidx))
2147                         return -1;
2148         }
2149 
2150         /*----- binary search the index -----*/
2151         lo = 0;
2152         hi = (int)tidx->page_ct -1;
2153         while (lo <= hi)
2154         {
2155                 mid = (lo + hi) /2;
2156                 term = *(tidx->index + mid);
2157                 d = trmncmp(stem,term,len);
2158                 if (d == 0)
2159                         break;
2160                 if (d < 0)
2161                         hi = mid-1;
2162                 else
2163                         lo = mid+1;
2164         }
2165 
2166         for ( ; d <= 0 && mid >0; --mid)
2167         {
2168                 if (d==0 && len == (int)strlen(term))
2169                         break;
2170                 term = *(tidx->index + (mid-1));
2171                 d = trmncmp(stem,term,len);
2172         }
2173         return mid;
2174 }
2175 
2176 
2177 static CdTerm* TrmIndex_GetCdTerm (TrmIndex *tidx, const char *term)
2178 {
2179         CdTerm *trm = NULL;
2180         int page_num;
2181         
2182         ASSERT(tidx != NULL);
2183         
2184         if ((page_num = TrmIndex_Lookup(tidx,term)) >=0)
2185         {
2186                 unsigned long cache_key = TIDX_KEY(tidx->type,tidx->fld,page_num);
2187                 Byte *page = Cache_Lock(_trm_cache,cache_key);
2188                 int missed;
2189                 if (page != NULL)
2190                         missed = FALSE;
2191                 else
2192                 {
2193                         missed = TRUE;
2194                         page = TrmIndex_ReadPage(tidx,page_num);
2195                 }
2196                 if (page != NULL)
2197                 {
2198                         Byte *ptr = page +1;
2199                         int flags, len, d;
2200                         
2201                         while ((flags = *ptr++) != 0)
2202                         {
2203                                 len = *ptr++;
2204                                 d = trmcmp(term,(char*)ptr);
2205                                 if (d == 0)
2206                                 {
2207                                         ptr -= 2;
2208                                         trm = getcdtrm(tidx,page_num,&ptr);
2209                                         break; 
2210                                 }
2211                                 ptr += len;
2212                                 if (flags & 0x01)  ptr +=4;
2213                                 if (flags & 0x02)  ptr +=4;
2214                                 ptr += 4;
2215                         }
2216                         
2217                         if (missed)
2218                                 Cache_Insert(_trm_cache,cache_key,page);
2219                         else
2220                                 Cache_Unlock(_trm_cache,cache_key);
2221                 }
2222         }
2223         return trm;
2224 }
2225 
2226 
2227 static int TrmIndex_ScanPages (TrmIndex *tidx, int start, int count, CdTermProc proc)
2228 {
2229         int page_num = start;
2230         int cancel = FALSE;
2231         int ct;
2232         
2233         /*----- load the index, if necessary -----*/
2234         if (tidx->index == NULL)
2235         {
2236                 if (!TrmIndex_ReadPageMap(tidx))
2237                         return 0;
2238         }
2239 
2240         for (ct=0; ct<count && page_num<tidx->page_ct && !cancel; ++ct, ++page_num)
2241         {
2242                 unsigned long cache_key = TIDX_KEY(tidx->type,tidx->fld,page_num);
2243                 Byte *page = Cache_Lock(_trm_cache,cache_key);
2244                 int missed;
2245                 if (page != NULL)
2246                         missed = FALSE;
2247                 else
2248                 {
2249                         missed = TRUE;
2250                         page = TrmIndex_ReadPage(tidx,page_num);
2251                 }
2252                 if (page != NULL)
2253                 {
2254                         Byte *ptr = page +1;
2255                         CdTerm *trm;
2256                         while ((trm = getcdtrm(tidx,page_num,&ptr)) != NULL)
2257                         {
2258                                 if (!(*proc)(trm))
2259                                 {
2260                                         cancel = TRUE;
2261                                         break;
2262                                 }
2263                         }
2264                 }
2265                 if (missed)
2266                         Cache_Insert(_trm_cache,cache_key,page);
2267                 else
2268                         Cache_Unlock(_trm_cache,cache_key);
2269         }
2270         return ct;
2271 }
2272 
2273 
2274 static FILE * TrmIndex_PostingsFileOpen (TrmIndex *tidx)
2275 {
2276         char fname[32];
2277         FILE *fd;
2278         
2279         ASSERT(tidx != NULL);
2280 
2281         sprintf(fname,"%s%s.pst",TYPTAG(tidx->type),FLDTAG(tidx->fld));
2282         if ((fd = CdEntrez_FileOpen(CdDir_trm,tidx->type,0,PST,fname,"rb")) ==NULL)
2283                 return NULL;
2284         return fd;
2285 }
2286 
2287 
2288 #define DECODE_LONG(ptr,val)    { int i;    \
2289                 for (i=0, val = 0; i<4; ++i)        \
2290                 { val <<= 8; val |= (unsigned long)*ptr++;      } } 
2291 
2292 
2293 static CdTerm* getcdtrm (TrmIndex *tidx, int pagenum, Byte **pptr)
2294 {
2295         CdTerm *trm;
2296         Byte *ptr = *pptr;
2297         int flags = *ptr++;
2298         if (flags != 0)
2299         {
2300                 int len = *ptr++;
2301                 char *str = MemGet(1+len,MGET_ERRPOST);
2302                 if (str!=NULL && (trm = MemNew(sizeof(CdTerm))) != NULL)
2303                 {
2304                         unsigned long val;
2305                         int i;
2306                         ASSERT((flags & 0xF0) ==0xE0);
2307                         trm->type = tidx->type;
2308                         trm->field = tidx->fld;
2309                         trm->page = pagenum;
2310                         trm->term = str;
2311                         for (i=0; i<len; ++i)
2312                                 *str++ = (char) *ptr++;
2313                         *str = '\0';
2314                                         
2315                         if (flags & 0x01)
2316                         {
2317                                 DECODE_LONG(ptr,val);
2318                                 trm->total_count = (long)val;
2319                         }
2320                         if (flags & 0x02)
2321                         {
2322                                 DECODE_LONG(ptr,val);
2323                                 trm->special_count = (long)val;
2324                         }
2325                         DECODE_LONG(ptr,val);
2326                         trm->offset = (long)val;
2327                         *pptr = ptr;
2328                         return trm;
2329                 }
2330         }
2331         return NULL;
2332 }
2333 
2334 static int trmcmp (const char *term1, const char *term2)
2335 {
2336         const char *p1 = term1;
2337         const char *p2 = term2;
2338         int c1 =0, c2 =0;
2339         
2340         while (*p1 && *p2)
2341         {
2342                 c1 = *p1++;
2343                 if (isalpha(c1))  c1 = tolower(c1);
2344                 c2 = *p2++;
2345                 if (isalpha(c2))  c2 = tolower(c2);
2346                 if (c1 != c2)
2347                 {
2348                         if (c1 == '/')
2349                                 return (c2 == '/') ? 0 : -1;
2350                         else if (c2 == '/')
2351                                 return 1;
2352                         return (c1 - c2);
2353                 }
2354         }
2355         return ((int)*p1 - (int)*p2);
2356 }
2357 
2358 static int trmncmp (const char *term1, const char *term2, int n)
2359 {
2360         const char *p1 = term1;
2361         const char *p2 = term2;
2362         int i, c1, c2;
2363         
2364         for (i=0; i<n; ++i)
2365         {
2366                 c1 = *p1++;
2367                 if (isalpha(c1))  c1=tolower(c1);
2368                 c2 = *p2++;
2369                 if (isalpha(c2))  c2=tolower(c2);
2370                 if (c1==0 || c2==0 || c1!=c2)
2371                         break;
2372         }
2373         if (i==n) 
2374                 return 0;
2375         if (c1 == '/')
2376                 return (c2 == '/') ? 0 : -1;
2377         else if (c2 == '/')
2378                 return 1;
2379         return (c1 - c2);
2380 }
2381 
2382 
2383 
2384 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
2385 /* CdVolume Functions.        Schuler 05-21-94                                 */
2386 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
2387 
2388 static CdVolume* CdVolume_Construct (CdVolume *cdvol, int number)
2389 {
2390         if (cdvol != NULL)
2391         {
2392                 char volname[16];
2393                 int i;
2394                 CdDevice *cddev;
2395                 
2396                 sprintf(volname,"entrez%d",number);
2397                 cdvol->volume_name = StrSave(volname);
2398                 cdvol->volume_num = number;
2399                 for (i=0, cddev=_cddev; i<_device_ct; ++i, ++cddev)
2400                 {
2401                         if (cddev->bound_cd == number)
2402                         {
2403                                 CdVolume_Mount(cdvol,cddev,TRUE);
2404                                 break;
2405                         }
2406                         if (cddev->hint == number)
2407                         {
2408                                 cddev->hint = 0;
2409                                 CdVolume_Mount(cdvol,cddev,FALSE);
2410                                 break;
2411                         }
2412                 }
2413         }
2414         return cdvol;
2415 }
2416 
2417 
2418 static CdVolume* CdVolume_Destruct (CdVolume *cdvol)
2419 {
2420         if (cdvol != NULL)
2421         {
2422                 MemFree((void*)cdvol->volume_name);
2423                 cdvol->volume_name = NULL;
2424         }
2425 
2426         return cdvol;
2427 }
2428 
2429 
2430 static CdDevice* GetLruDevice (void)
2431 {
2432         CdDevice *dev_lru =NULL;
2433         time_t time_lru;
2434         CdDevice *dev;
2435         int i;
2436         
2437         for (i=0, dev=_cddev; i<_device_ct; ++i, ++dev)
2438         {
2439                 if (dev->is_ejectable)
2440                 {
2441                         if (dev->volume == NULL)
2442                         {
2443                                 dev_lru = dev;
2444                                 break;
2445                         }
2446                         if (dev_lru == NULL || dev->timestamp < time_lru)
2447                         {
2448                                 dev_lru = dev;
2449                                 time_lru = dev->timestamp;
2450                         }
2451                 }
2452         }
2453         if (dev_lru != NULL && dev_lru->volume != NULL)
2454         {
2455                 CdVolume *vol_to_eject = dev_lru->volume;
2456                 CdVolume_Unmount(dev_lru->volume);
2457                 if (dev_lru->is_ejectable)
2458                 {
2459                         if (!(*_hookEject)(vol_to_eject->volume_name,&dev_lru->inf))
2460                                 ErrPostEx(SEV_INFO,0,0,"Unable to eject %s",vol_to_eject->volume_name);
2461         }
2462         }
2463         return dev_lru;
2464 }
2465 
2466 
2467 static CdDevice* CdVolume_Mount (CdVolume *cdvol, CdDevice *cddev, int verify)
2468 {
2469         ASSERT(cdvol != NULL);
2470         
2471         if (cddev == NULL)
2472         {
2473                 cddev = GetLruDevice();
2474                 ASSERT(cddev != NULL);
2475                 ASSERT(cddev->volume == NULL);
2476         }
2477 
2478         if (!cddev->is_ejectable)
2479         {
2480                 if (cdvol->device != NULL)
2481                 {
2482                         ErrPostEx(SEV_INFO,0,0,"CdVolume_Mount;  Device busy");
2483                         return NULL;
2484                 }
2485                 if (!cddev->is_inited)
2486                 {
2487                         if (!CdDevice_Init(cddev))  return NULL;
2488                 }
2489                 cdvol->device = cddev;
2490                 cddev->volume = cdvol;
2491         }
2492         else
2493         {       
2494                 while (cdvol->device == NULL)
2495                 {
2496                         if (verify)
2497                         {
2498                                 if (! (*_hookInsert)(cdvol->volume_name,&cddev->inf))
2499                                         return NULL;
2500                         }
2501         
2502                         if (!cddev->is_inited)
2503                         {
2504                                 if (CdDevice_Init(cddev) && cddev->hint == cdvol->volume_num)
2505                                 {
2506                                         cdvol->device = cddev;
2507                                         cddev->volume = cdvol;
2508                                 }
2509                         }
2510                         else if (verify)
2511                         {
2512                                 char fpath[256];
2513                                 CdRomInfo  info;
2514                                 if (cddev->inf.root == NULL)
2515                                         fpath[0] = '\0';
2516                                 else
2517                                         strcpy(fpath,cddev->inf.root);
2518                                 if (cddev->ins_volname)
2519                                         FileBuildPath(fpath,cdvol->volume_name,NULL);
2520                                 if (!CdTestPath(fpath,&info))
2521                                 {
2522                                         ErrLogPrintf("CdTestPath [%s] FAILED\n",fpath);
2523                                         ErrPostEx(SEV_INFO,0,0,"Unable to get CD-ROM volume info");
2524                                 }
2525                                 else
2526                                 {
2527                                         /*ErrLogPrintf("CdTestPath [%s] OK; rel %d.%d; cd %d of %d\n", fpath,
2528                                                                 info.rel_major, info.rel_minor, info.cd_num, info.cd_count);*/
2529                                         cddev->upper_case = info.upper_case;
2530                                         cddev->semicolon_one = info.semicolon_one;
2531                                         if (info.rel_major != _rel_major || info.rel_minor != _rel_minor)
2532                                                 ErrPostEx(SEV_INFO,0,0,"The inserted CD-ROM is from the wrong release");
2533                                         else if (cdvol->volume_num == info.cd_num)
2534                                         {
2535                                                 cdvol->device = cddev;
2536                                                 cddev->volume = cdvol;
2537                                         }
2538                                 }
2539                                 if (cdvol->device == NULL && cddev->is_ejectable)
2540                                 {
2541                                         char volume_to_eject[32];
2542                                         volume_to_eject[0] = '\0';
2543                                         if (cddev->ins_volname)
2544                                         {
2545                                                 int ct, freecds[CDVOL_MAX];
2546                                                 ct = FindFloatingEntrezVolumes(freecds);
2547                                                 if (ct > 0)
2548                                                         sprintf(volume_to_eject,"entrez%d",freecds[0]);
2549                                         }
2550                                         else if (cdvol->volume_name != NULL)
2551                                                 strcpy(volume_to_eject,cdvol->volume_name);
2552 
2553                                         if (!(*_hookEject)(volume_to_eject,&cddev->inf))
2554                                                 ErrPostEx(SEV_INFO,0,0,"Unable to eject %s",cdvol->volume_name);
2555                         }
2556                         }
2557                         else
2558                         {
2559                                 cdvol->device = cddev;
2560                                 cddev->volume = cdvol;
2561                         }
2562                 }
2563         }
2564         CdDevice_Touch(cdvol->device);
2565         return cdvol->device;
2566 }
2567 
2568 
2569 static int  CdVolume_Unmount (CdVolume *cdvol)
2570 {
2571         CdDevice *cddev;
2572         
2573         ASSERT(cdvol != NULL);
2574 
2575         cddev=cdvol->device;
2576         if (cddev != NULL && cddev->is_ejectable)
2577         {
2578                 cddev->volume = NULL;
2579                 cdvol->device = NULL;
2580                 CdDevice_Touch(cddev);
2581                 return TRUE;
2582         }
2583         return FALSE;
2584 }
2585 
2586 
2587 static int CdVolume_IsMounted (CdVolume *cdvol)
2588 {
2589         ASSERT(cdvol != NULL);
2590         return cdvol->device != NULL;
2591 }
2592 
2593 
2594 static int FindFloatingEntrezVolumes (int *outlist)
2595 {
2596         int i, ct=0;
2597         char testpath[16];
2598         CdRomInfo info;
2599 
2600         for (i=0; i<_volume_ct; ++i)
2601         {
2602                 if (_cdvol[i].device == NULL)
2603                 {
2604                         sprintf(testpath,"entrez%d",i+1);
2605                         if (CdTestPath(testpath,&info))
2606                         {
2607                                 VERBOSE("FindFloatingEntrezVolumes: Entrez%d found\n",info.cd_num);
2608                                 outlist[ct++] = info.cd_num;
2609                         }
2610                 }
2611         }
2612         return ct;
2613 }
2614 
2615 
2616 
2617 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
2618 /* CdDevice Functions         Schuler 05-21-94                                 */
2619 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
2620 
2621 static CdDevice* CdDevice_Construct (CdDevice *cddev, int number)
2622 {
2623         if (cddev != NULL)
2624         {
2625                 char section[32];
2626                 char buffer[64];
2627         
2628                 sprintf(section,"CdEntrez.Device.%d",number);
2629                 cddev->inf.root = GetAppParamStr(_rcfile,section,"Root",NULL);
2630                 cddev->inf.formal_name = GetAppParamStr(_rcfile,section,"Formal_Name",NULL);
2631                 cddev->inf.device_name = GetAppParamStr(_rcfile,section,"Device_Name",NULL);
2632                 cddev->inf.raw_device_name = GetAppParamStr(_rcfile,section,"Raw_Device_Name",NULL);
2633                 cddev->inf.mount_point = GetAppParamStr(_rcfile,section,"Mount_Point",NULL);
2634                 cddev->inf.mount_cmd = GetAppParamStr(_rcfile,section,"Mount_Cmd",NULL);
2635                 cddev->is_ejectable = GetAppParamBoolean(_rcfile,section,"Ejectable",TRUE);
2636                 cddev->ins_volname = GetAppParamBoolean(_rcfile,section,"Insert_Volname",FALSE);
2637 
2638                 if (GetAppParam(_rcfile,section,"Bind",NULL,buffer,sizeof buffer))
2639                 {
2640                         if (StrNICmp(buffer,"entrez",6) ==0)
2641                                 cddev->bound_cd = atoi(buffer+6);
2642                 }
2643         }
2644         return cddev;
2645 }
2646 
2647 static CdDevice* CdDevice_Destruct (CdDevice *cddev)
2648 {
2649         if (cddev != NULL)
2650         {
2651                 MemFree((void*)cddev->inf.root);
2652                 MemFree((void*)cddev->inf.formal_name);
2653                 MemFree((void*)cddev->inf.device_name);
2654                 MemFree((void*)cddev->inf.raw_device_name);
2655                 MemFree((void*)cddev->inf.mount_point);
2656                 MemFree((void*)cddev->inf.mount_cmd);
2657         }
2658         return cddev;
2659 }
2660 
2661 
2662 static int CdDevice_FileBuildPath (CdDevice *cddev, char *fpath, const char *fdir, const char *fname)
2663 {
2664         ASSERT(cddev != NULL);
2665         ASSERT(fpath != NULL);
2666         
2667         *fpath = '\0';
2668         if (cddev->is_inited)
2669         {
2670                 char *p;
2671                 
2672                 if (cddev->inf.root != NULL)
2673                         strcpy(fpath,cddev->inf.root);
2674                 if (cddev->ins_volname)
2675                 {
2676                         char volname[16];
2677                         volname[0] = '\0';
2678                         if (cddev->volume != NULL)
2679                                 strncat(volname,cddev->volume->volume_name,sizeof volname);
2680                         else
2681                                 sprintf(volname,"entrez%d",cddev->hint);
2682                         if (!FileBuildPath(fpath,volname,NULL))
2683                                 return FALSE;
2684                 }
2685                 VERIFY(p = strchr(fpath,'\0'));
2686                 if (!FileBuildPath(fpath,(char*)fdir,(char*)fname))
2687                         return FALSE;
2688                 if (cddev->upper_case)
2689                         StrUpper(p);
2690                 if (cddev->semicolon_one)
2691                         strcat(p,";1");
2692                 return TRUE;
2693         }
2694         return FALSE;   
2695 }
2696 
2697 
2698 static FILE * CdDevice_FileOpen (CdDevice *cddev, const char *fdir, const char *fname, const char *fmode)
2699 {
2700         char fpath[256];
2701         char ldir[32];
2702         char lname[32];
2703         FILE *fd;
2704         
2705         ASSERT(cddev != NULL);
2706         
2707         if (cddev->volume == NULL)      
2708                 return NULL;
2709         
2710         if (!cddev->is_inited)
2711         {
2712                 if (!CdDevice_Init(cddev))
2713                         return NULL;
2714         }
2715         if (cddev->inf.root == NULL)
2716                 fpath[0] = '\0';
2717         else
2718                 strcpy(fpath,cddev->inf.root);
2719         if (cddev->ins_volname)
2720                 FileBuildPath(fpath,cddev->volume->volume_name,NULL);
2721         strcpy(ldir,fdir);
2722         strcpy(lname,fname);
2723         if (cddev->upper_case)
2724         {
2725                 StrUpper(ldir);
2726                 StrUpper(lname);
2727         }
2728         FileBuildPath(fpath,ldir,lname);
2729         if (cddev->semicolon_one)
2730                 strcat(fpath,";1");
2731         if ((fd = FileOpen(fpath,fmode)) != NULL)
2732                 CdDevice_Touch(cddev);
2733         return fd;
2734 }
2735 
2736 
2737 static int CdDevice_Init (CdDevice *cddev)
2738 {                                    
2739         int cd_num;
2740         char fpath[256];
2741         CdRomInfo info;
2742         
2743         ASSERT(cddev != NULL);
2744         if (cddev->is_inited)
2745                 return TRUE;
2746 
2747         cd_num = cddev->bound_cd;
2748         memset((void*)&info,0,sizeof info);
2749         fpath[0] = '\0';
2750         if (cddev->inf.root !=NULL)
2751                 strncat(fpath,cddev->inf.root,sizeof fpath);
2752                 
2753         if (cddev->ins_volname)
2754         {
2755                 char volname[16];
2756                 if (cddev->bound_cd !=0)
2757                 {
2758                         sprintf(volname,"entrez%d",cddev->bound_cd);
2759                         FileBuildPath(fpath,volname,NULL);
2760                 }
2761                 else                            
2762                 {
2763                         int vol_ct = (_volume_ct ==0) ? 3 : _volume_ct;
2764                         int j;
2765                         for (j=0; j<vol_ct; ++j)
2766                         {
2767                                 fpath[0] = '\0';
2768                                 if (cddev->inf.root !=NULL)
2769                                         strncat(fpath,cddev->inf.root,sizeof fpath);
2770                                 sprintf(volname,"entrez%d",j+1);
2771                                 FileBuildPath(fpath,volname,NULL);
2772                                 if (CdTestPath(fpath,&info))
2773                                         break;
2774                         }
2775                 }
2776         }
2777 
2778         if (info.cd_num == 0)
2779         {
2780                 if (!(CdTestPath(fpath,&info)))
2781                         return FALSE;
2782         }
2783                 
2784         cddev->upper_case = info.upper_case;
2785         cddev->semicolon_one = info.semicolon_one;
2786         cddev->hint = info.cd_num;
2787         cddev->is_inited = TRUE;
2788         CdDevice_Touch(cddev);
2789         return TRUE;
2790 }
2791 
2792 
2793 static void  CdDevice_Touch (CdDevice *cddev)
2794 {
2795         time_t timestamp = time(NULL);
2796         ASSERT(cddev != NULL);
2797         cddev->timestamp = timestamp;
2798         if (cddev->volume != NULL)
2799                 cddev->volume->timestamp = timestamp;
2800 }
2801 
2802 
2803 static int LIBCALLBACK default_CdInsertProc (const char *volname, const CdDevInfo *dev)
2804 {
2805         char msg[80];
2806         
2807         ASSERT(volname != NULL);
2808         ASSERT(dev != NULL);
2809         
2810         sprintf(msg,"Please insert volume \"%s\"",volname);
2811         if (dev->formal_name)
2812                 sprintf(strchr(msg,0)," into %s",dev->formal_name);
2813         if (MsgAlert(KEY_OKC,SEV_INFO,GetProgramName(),msg) == ANS_CANCEL)
2814                 return FALSE;
2815         if (dev->mount_cmd != NULL)
2816         {
2817                 return MountCd((char*)volname, dev->device_name, 
2818                                                 dev->mount_point, dev->mount_cmd);
2819         }
2820         return TRUE;
2821 }
2822 
2823 
2824 static int LIBCALLBACK default_CdEjectProc (const char *volname, const CdDevInfo *dev)
2825 {
2826   return EjectCd((char*)volname, dev->device_name, 
2827                  dev->raw_device_name, dev->mount_point, dev->mount_cmd);
2828 }
2829 
2830 
2831 
2832 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
2833 /* CdMap   Schuler 7-29-94                                                     */
2834 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
2835 
2836 static CdMap* CdMap_Construct (CdMap *map, int array_size)
2837 {
2838         if (map != NULL)
2839         {   
2840                 map->array_size = (short)array_size;
2841                 if (array_size > 0)
2842                         map->list = (CdMap*) MemNew((size_t)array_size * sizeof(CdMap));
2843         }
2844         return map;
2845 }
2846 
2847 static CdMap* CdMap_Destruct (CdMap *map)
2848 {
2849         if (map != NULL)
2850         {
2851                 CdMap *m;
2852                 if (map->lnklist)
2853                 {
2854                         CdMap *m2;
2855                         for (m=map->list; m; m=m2)
2856                         {
2857                                 m2 = m->list;
2858                                 m->list = NULL;
2859                                 CdMap_Free(m);
2860                         }
2861                 }
2862                 else
2863                 {
2864                         int i;
2865                         for (i=0, m=map->list; i<map->array_size; ++i, ++m)
2866                                 CdMap_Destruct(m);
2867                         MemFree((void*)map->list);
2868                 }
2869                 MemFree((void*)map->cd_list);
2870                 MemFree((void*)map->user_path);
2871                 memset((void*)map,0,sizeof(CdMap));
2872         }
2873         return map;
2874 }
2875 
2876 
2877 static void CdMap_ParseCdNums (CdMap *map, char *nums)
2878 {
2879         int cd, n=0;
2880         short cd_list[8];
2881         char *p, *p2 = nums;
2882                 
2883         ASSERT(map != NULL);
2884         ASSERT(nums != NULL);
2885 
2886         while ((p = p2) != NULL)
2887         {
2888                 if ((p2 = strpbrk(p,",\10")) != NULL)
2889                         *p2++ = '\0';
2890                 cd = atoi(p);
2891                 ASSERT(VOLNUM_IS_VALID(cd));
2892                 cd_list[n++] = (short) cd;
2893         }
2894         
2895         if (n > 1)
2896         {
2897                 map->multicd = TRUE;
2898                 map->cd_num = n;
2899                 map->cd_list = (short*) MemGet(n*sizeof(short),MGET_ERRPOST);
2900                 ASSERT(map->cd_list != NULL);
2901                 memcpy((void*)map->cd_list,(void*)cd_list,n*sizeof(short));
2902         }
2903         else
2904         {
2905                 map->multicd = FALSE;
2906                 map->cd_num = cd_list[0];
2907                 map->cd_list = NULL;
2908         }
2909 }
2910 
2911 
2912 static CdMap* CdMap_GetChild (CdMap *map, int nkid)
2913 {
2914         ASSERT(map != NULL);
2915         
2916         if (map->lnklist)
2917         {
2918                 CdMap *m2;
2919                 for (m2=map->list; m2; m2=m2->list)
2920                 {
2921                         if (m2->id_num == nkid)
2922                                 return m2;
2923                 }
2924         }       
2925         else if (map->list != NULL)
2926         {
2927                 return &(map->list[nkid]);
2928         }
2929         return NULL;
2930 }
2931 
2932 
2933 static int CdMap_GetSpecs (CdMap *map, int *cdlist, char *fdir)
2934 {
2935         int count =0;
2936 
2937         ASSERT(map != NULL);
2938         ASSERT(cdlist != NULL);
2939         ASSERT(fdir != NULL);
2940         
2941         if (map->user_path != NULL)
2942                 strcpy(fdir,map->user_path);
2943                 
2944         if (map->cd_num != 0)
2945         {
2946                 if (map->multicd)
2947                 {
2948                         int i;
2949                         count = map->cd_num;
2950                         ASSERT(map->cd_num < CDVOL_MAX);
2951                         for (i=0; i<count; ++i)
2952                         {
2953                                 cdlist[i] = map->cd_list[i];
2954                         }
2955                 }
2956                 else
2957                 {
2958                         count = 1;
2959                         *cdlist = map->cd_num;
2960                 }
2961         }
2962         return count;
2963 }
2964 
2965 
2966 static void CdMap_FindPath (CdMap *map, const char *key)
2967 {
2968         char fpath[256];
2969         if (FindPath(_rcfile,"CdEntrez.Paths",(char*)key,fpath,sizeof fpath))
2970         {
2971                 map->user_set = TRUE;
2972                 map->user_path = StrSave(fpath);
2973         }
2974 }
2975 
2976 
2977 
2978 
2979 static Boolean ReadCdLayout (FILE *fd)
2980 {                                              
2981         CdEntrezDir dir;
2982         char line[64], key[32];
2983         int i,j;
2984                 
2985         for (dir=CdDir_FIRST; dir<=CdDir_LAST; ++dir)
2986         {
2987                 CdMap_FindPath(&_map[dir],_dir[dir].key);
2988                 if (dir == CdDir_sys)
2989                         CdMap_Construct(&_map[dir],0);
2990                 else
2991                 {
2992                         CdMap_Construct(&_map[dir],_type_ct);
2993                         for (i=0; i<_type_ct; ++i)
2994                         {
2995                                 CdMap_Construct(&_map[dir].list[i],0);
2996                                 sprintf(key,"%s-%s",_dir[dir].key,TYPTAG(i));
2997                                 CdMap_FindPath(&_map[dir].list[i],key);
2998                         }
2999                 }
3000         }
3001         
3002         while (UnixFileGets(line,sizeof line,fd))
3003         {
3004                 if (isalpha(line[0]))
3005                 {
3006                         char *pt, *pd, *pn;
3007                         CdMap *map = NULL;
3008 
3009                         VERIFY((pn = strchr(line,'\t')) != NULL);
3010                         *pn++ = '\0';   /* pn points to the number(s) */
3011                         
3012                         if ((pt = strchr(line,'-')) != NULL)
3013                                 *pt++ = '\0';    /* pt points to the type tag */
3014 
3015                         for (dir=CdDir_FIRST; dir<=CdDir_LAST; ++dir)
3016                         {
3017                                 if (strcmp(line,_dir[dir].key)==0)
3018                                         break;
3019                         }
3020                         ASSERT(dir <= CdDir_LAST);
3021                         map = &_map[dir];
3022 
3023                         if (pt != NULL)
3024                         {
3025                                 if ((pd = strchr(pt,'-')) != NULL)
3026                                         *pd++ = '\0';   /* pd points to the division tag */
3027 
3028                                 for (i=0; i<_type_ct; ++i)
3029                                 {
3030                                         if (strcmp(pt,TYPTAG(i)) ==0)
3031                                                 break;
3032                                 }
3033                                 ASSERT(i < _type_ct);
3034                                 map = &(map->list[i]);
3035                                 
3036                                 if (pd != NULL)
3037                                 {
3038                                         /* lookup division tag */
3039                                         for (j=0; j<_div_ct; ++j)
3040                                         {
3041                                                 if (strcmp(pd,DIVTAG(j)) ==0)
3042                                                         break;
3043                                         }
3044                                         ASSERT(j < _div_ct);
3045                                         if (_cdinfo->div_info[j].docs[i] != 0)
3046                                         {
3047                                                 CdMap *m2;
3048                                                 
3049                                                 /* create node and link into list */
3050                                                 map->lnklist = TRUE;
3051                                                 m2 = CdMap_New(0);
3052                                                 m2->id_num = j +1;
3053                                                 m2->list = map->list;
3054                                                 map->list = m2;
3055                                                 map = m2;
3056                                                 sprintf(key,"%s-%s-%s",_dir[dir].key,TYPTAG(i),DIVTAG(j));
3057                                                 CdMap_FindPath(map,key);
3058                                         }
3059                                 }
3060                         }
3061                         CdMap_ParseCdNums(map,pn);
3062                 }
3063         }
3064         
3065         if (_map[CdDir_sys].cd_num == 0)
3066         {
3067                 /* The list of filenames and filesizes for the sysinfo directory
3068                         was inadvertently omitted from the cdlayout.inf file on the 
3069                         sample CD-ROMs distributed to developers.  This section of
3070                         code should never be executed in the final release. */
3071 
3072                 VERBOSE("   HACK (faking cdlayout.inf information for SYSINFO directory)\n");
3073                 _map[CdDir_sys].cd_list = (short*) MemNew(_volume_ct * sizeof(short));
3074                 for (i=0; i<_volume_ct; ++i)
3075                 {
3076                         _map[CdDir_sys].cd_list[i] = i +1;
3077                 }
3078                 _map[CdDir_sys].cd_num = _volume_ct;
3079                 _map[CdDir_sys].multicd = TRUE;
3080         }
3081         
3082         return TRUE;
3083 }
3084 
3085 
3086 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
3087 /* LSet (similar to LinkSet)  Schuler 06-01-94                                 */
3088 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
3089 #define FLUFF 2
3090 
3091 static LSet* LSet_Construct (LSet *lset, short sorc_type, short dest_type, int link_max)
3092 {
3093         if (lset != NULL)
3094         {
3095                 lset->link = (DocLink*) Malloc(FLUFF*sizeof(DocLink));
3096                 if (lset->link == NULL)
3097                 {
3098                         MemFree((void*)lset);
3099                         return NULL;
3100                 }
3101                 lset->slots = FLUFF;
3102                 lset->sorc_type = sorc_type;
3103                 lset->dest_type = dest_type;
3104                 lset->link_max = link_max;
3105         }
3106         return lset;
3107 }
3108 
3109 static LSet * LSet_Destruct (LSet *lset)
3110 {
3111         if (lset != NULL)       
3112                 Free((void*)lset->link); 
3113         return lset;
3114 }
3115 
3116 
3117 static int LSet_Read (LSet *lset, FILE *fd)
3118 {
3119         unsigned short m[TYPE_MAX];
3120         DocUid uid;
3121         int link_ct, max_ct;
3122         int i, j, k, wt;
3123         DocLink *link, *p1, *p2, *p3;
3124         
3125         ASSERT(lset != NULL);
3126         link = lset->link;
3127         ASSERT(link != NULL);
3128 
3129         /* read the link counts */
3130         FileReadSwapShort(m,_type_ct,fd);
3131         link_ct = m[lset->dest_type];
3132         max_ct = MIN(lset->link_max,lset->count+link_ct);
3133 
3134         /* grow the array, if necessary */
3135         if (max_ct > lset->slots)
3136         {
3137                 int cd3_slots = max_ct + FLUFF;
3138                 size_t bytes = sizeof(DocLink) * cd3_slots;
3139                 void *array = Realloc((void*)link,bytes);
3140                 if (array == NULL)
3141                 {
3142                         ErrPostEx(SEV_FATAL,0,0,"Out of memory");
3143                         return FALSE;
3144                 }
3145                 lset->link = link = (DocLink*)array;
3146                 lset->slots = max_ct + FLUFF;
3147         }
3148         
3149         /* skip over link lists for other types that preceed this one */
3150         for (i=0; i<lset->dest_type; ++i)
3151         {
3152                 fseek(fd,4*m[i],SEEK_CUR);
3153                 if (i==lset->sorc_type)
3154                         fseek(fd,m[i],SEEK_CUR);
3155         }
3156 
3157         /* process links, one at a time */      
3158         for (i=0; i<link_ct; ++i)
3159         {
3160                 /* read next link from the file */
3161                 /* NEED TO CHECK FOR ERROR HERE! */
3162                 FileReadSwapInt4((Uint4*)&uid,1,fd);
3163                 wt = (lset->sorc_type == lset->dest_type) ? fgetc(fd) : 1;
3164                 if (lset->count == lset->link_max)  continue;
3165                 
3166                 /* insert into array, keeping sorted by UID and summing weights */
3167                 for (j=0, p1=link; j<lset->count; ++j, ++p1)
3168                 {
3169                         if (p1->uid == uid)
3170                                 break;
3171                         if (p1->uid > uid)
3172                         {
3173                                 p2 = link + lset->count;
3174                                 p3 = p2 -1;
3175                                 for (k=j; k<lset->count; ++k)
3176                                         *p2-- = *p3--;
3177                                 break;
3178                         }
3179                 }                
3180                 if (j==lset->count || p1->uid != uid)
3181                 {
3182                         p1->wt = 0;
3183                         lset->count ++;
3184                 }
3185                 p1->uid = uid;
3186                 p1->wt += wt;
3187         }
3188         return TRUE;
3189 }
3190 
3191 static int LIBCALLBACK linkcmp (VoidPtr ptr1, VoidPtr ptr2);
3192 /*
3193 static int linkcmp (const void *ptr1, const void *ptr2);
3194 */
3195 
3196 static LinkSet* LSet_Convert (LSet *lset)
3197 {
3198         LinkSet *lnkset = NULL;
3199         ASSERT(lset != NULL);
3200         if ((lnkset = (LinkSet*) MemNew(sizeof(LinkSet))) != NULL)
3201         {
3202                 int i;
3203                 DocLink *p = lset->link;
3204                 
3205                 /*
3206                 qsort((void*)p,lset->count,sizeof(DocLink),linkcmp);
3207                 */
3208                 HeapSort ((VoidPtr) p,lset->count,sizeof(DocLink),linkcmp);
3209                 lnkset->num = lset->count;
3210                 lnkset->uids = (Int4*) MemGet(sizeof(Int4)*lset->count, MGET_ERRPOST);
3211                 lnkset->weights = (Int4*) MemGet(sizeof(Int4)*lset->count, MGET_ERRPOST);
3212                 for (i=0; i<lset->count; ++i, ++p)
3213                 {
3214                         lnkset->uids[i] = p->uid;
3215                         lnkset->weights[i] = p->wt;
3216                 }
3217                 LSet_Free(lset);                
3218         }
3219         return lnkset;
3220 }
3221 
3222 static int LIBCALLBACK linkcmp (VoidPtr ptr1, VoidPtr ptr2)
3223 /*
3224 static int linkcmp (const void *ptr1, const void *ptr2)
3225 */
3226 {
3227         DocLink *lnk1 = (DocLink*)ptr1;
3228         DocLink *lnk2 = (DocLink*)ptr2;
3229         int d;
3230         
3231         if ((d = lnk2->wt - lnk1->wt) ==0)
3232                 d = (lnk1->uid > lnk2->uid) ? -1 : 1;
3233         return d;
3234 }
3235 
3236 
3237 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
3238 /* HuffTable Functions.   Schuler 06-13-94                                      */
3239 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
3240 
3241 static HuffTable* HuffTable_Construct (HuffTable *huff, int n)
3242 {
3243         if (huff != NULL)
3244         {
3245                 huff->count = n;
3246                 if ( ((huff->left = (short*)MemGet(sizeof(short)*n,MGET_ERRPOST)) != NULL)
3247                                 && ((huff->right= (short*)MemGet(sizeof(short)*n,MGET_ERRPOST)) != NULL) )
3248                         return huff;
3249                 huff = NULL;
3250         }
3251         return huff;
3252 }
3253 
3254 static HuffTable* HuffTable_Destruct (HuffTable *huff)
3255 {
3256         if (huff != NULL)
3257         {
3258                 MemFree((void*)huff->right);
3259                 MemFree((void*)huff->left);
3260                 memset((void*)huff,0,sizeof(HuffTable));
3261         }
3262         return huff;
3263 }
3264 
3265 static HuffTable* HuffTable_Read (FILE *fd)
3266 {
3267         short n;
3268         if (FileReadSwapShort((unsigned short*)&n,1,fd))
3269         {
3270                 HuffTable *huff = HuffTable_New(n);
3271                 if (huff != NULL)
3272                 {
3273                         if ( (FileReadSwapShort((unsigned short*)huff->left,n,fd) == n)
3274                                                 && FileReadSwapShort((unsigned short*)huff->right,n,fd) == n )
3275                         {
3276                                 return huff;
3277                         }
3278                         HuffTable_Free(huff);
3279                 }
3280         }
3281         return NULL;
3282 }
3283 
3284 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
3285 /* DecompInfo Functions     Schuler 06-21-94                                   */
3286 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
3287 
3288 static DecompInfo* DecompInfo_Construct (DecompInfo *info, HuffTable *huff)
3289 {
3290         if (info != NULL)
3291                 info->huff = huff;
3292         return info;
3293 }
3294 
3295 static AsnIo* DecompInfo_Attach (DecompInfo *info, FILE *fd)
3296 {
3297         AsnIo *aio = AsnIoNew(ASNIO_BIN_IN,fd,info,DecompReadProc,NULL);
3298         if (aio != NULL)
3299         {
3300                 info->aio = aio;
3301                 info->fd = fd;
3302                 return aio;
3303         }
3304         return NULL;
3305 }
3306 
3307 static FILE* DecompInfo_Detach (DecompInfo *info)
3308 {
3309         FILE *fd = info->fd;
3310         info->fd = NULL;
3311         info->aio->fp = NULL;
3312         AsnIoClose(info->aio);
3313         info->aio = NULL;
3314         return fd;
3315 }
3316 
3317 static Int2 LIBCALLBACK DecompReadProc (void *opaque, char *buff, Uint2 count)
3318 {
3319         DecompInfo *dcp = (DecompInfo*)opaque;
3320         register unsigned int mask = dcp->mask;
3321         register unsigned int byte = dcp->byte;
3322         char *p = buff;
3323         int i, cnt = 0;
3324         int c;
3325         int k;
3326         FILE *fd1 = dcp->fd;
3327         int sentinel = dcp->huff->count;
3328 
3329         while (cnt < (int) count)
3330         {
3331                 for (i=0; i>=0; )
3332                 {
3333                         if (mask == 0)
3334                         {
3335                                 if ((c = fgetc(fd1)) == EOF)
3336                                 {
3337                                         /* should never reach this point */
3338                                         ErrPostEx(SEV_INFO,0,0,"Unexpected EOF");
3339                                         i = sentinel - 257;
3340                                         break;
3341                                 }
3342                                 else
3343                                 {
3344                                         byte = (unsigned int) c;
3345                                         mask = 0x80;
3346                                 }
3347                         }
3348 
3349                         if (byte & mask)
3350                                 i = dcp->huff->left[i];
3351                         else
3352                                 i = dcp->huff->right[i];
3353 
3354                         mask >>= 1;
3355                 }
3356 
3357                 if ((k = i + 257) == sentinel)
3358                 {
3359                         mask = 0; /* to skip remaining bits in current byte */
3360                         break;
3361                 }
3362 
3363                 *p++ = (char) k;
3364                 cnt++;
3365         }
3366 
3367         dcp->mask = mask;
3368         dcp->byte = byte;
3369         return cnt;
3370 }
3371 
3372 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
3373 * CACHE FUNCTIONS.   Schuler 05-17-94
3374 *
3375 * Functions for caching arbitrary pages of data.  Pages are uniquely
3376 * identified by a long integer.
3377 *
3378 * MODIFICATIONS
3379 * When      Who       What
3380 * --------  --------  ------------------------------------------------------
3381 * 06-01-94  Schuler  Added new argument to Cache_New() that is a pointer to
3382 *                    a function to be called to free cached data items.
3383 * 
3384 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
3385 
3386 static CachePage * Cache_FindPage (Cache *cache, long id, int *index);
3387 static void Cache_Delete_ByIndex (Cache *cache, int index);
3388 static void Cache_Touch_ByIndex (Cache *cache, int index);
3389 static int Cache_DeleteLRU (Cache *cache);
3390 
3391 #define Cache_ISVALID(x)  ((x) != NULL && (x)->magic == Cache_MAGIC_VALUE )
3392 
3393 static int Cache_IsValid (Cache *cache)
3394 {
3395         return Cache_ISVALID(cache);
3396 }
3397 
3398 static void PASCAL DefCacheDataFreeProc (void *data)
3399 {
3400         MemFree(data);
3401 }
3402 
3403 static Cache* Cache_Construct (Cache *cache, int slots, CacheDataFreeProc fproc)
3404 {
3405         if (cache != NULL)
3406         {
3407                 size_t bytes = (size_t)slots * sizeof(CachePage);
3408                 CachePage *p = (CachePage*) MemGet(bytes,MGET_ERRPOST);
3409                 if (p == NULL)
3410                 {
3411                         cache = NULL;
3412                 }
3413                 else
3414                 {
3415                         cache->magic = Cache_MAGIC_VALUE;
3416                         cache->page = p;
3417                         cache->page_slots = slots;
3418                         cache->fproc = fproc;
3419                 }
3420         }
3421         return cache;
3422 }
3423 
3424 static Cache* Cache_Destruct (Cache *cache)
3425 {
3426         if (cache != NULL)
3427         {
3428                 int i;
3429                 CacheDataFreeProc fproc;
3430                 
3431                 ASSERT(Cache_ISVALID(cache));
3432         
3433                 if ((fproc = cache->fproc) != NULL)
3434                 {
3435                         for (i=0; i<cache->page_count; ++i)
3436                                 (*fproc)(cache->page[i].data);
3437                 }
3438                 (void)MemFree((void*)cache->page);
3439                 cache->magic = 0;
3440         }
3441         return cache;
3442 }
3443 
3444 static CachePage * Cache_FindPage (Cache *cache, long id, int *index)
3445 {
3446         CachePage *p = cache->page;
3447         int i;
3448         for (i=0; i<cache->page_count; ++i, ++p)
3449         {
3450                 if (p->id == id)
3451                 {
3452                         if (index != NULL)
3453                                 *index = i;
3454                         return p;
3455                 }
3456         }
3457         return NULL;
3458 }
3459 
3460 static int Cache_Insert (Cache *cache, long id, void *data)
3461 {
3462     int i;
3463     
3464         ASSERT(Cache_ISVALID(cache));
3465 
3466         if ((Cache_FindPage(cache,id,NULL)) != NULL)
3467         {
3468                 ErrPostEx(SEV_INFO,0,0,"Cache_Insert;  Duplicate IDs");
3469                 return FALSE;
3470         }
3471         
3472         if (cache->page_count == cache->page_slots)
3473         {
3474                 if (!Cache_DeleteLRU(cache))
3475                 {
3476                         ErrPostEx(SEV_INFO,0,0,"Cache_Insert;  All pages locked");
3477                         return FALSE;
3478                 }
3479         }
3480         
3481         i = cache->page_count;
3482         cache->page[i].id = id;
3483         cache->page[i].lock = 0;
3484         cache->page[i].data = data;
3485         cache->page_count ++;
3486         if (i > 0)
3487                 Cache_Touch_ByIndex(cache,i);
3488         return TRUE;
3489 }
3490 
3491 static void Cache_Delete_ByIndex (Cache *cache, int index)
3492 {
3493         CacheDataFreeProc fproc = cache->fproc;
3494         CachePage temp = cache->page[index];
3495         int i;
3496         cache->page_count--;
3497         for (i=index; i<cache->page_count; ++i)
3498         {
3499                 cache->page[i] = cache->page[i+1];
3500         }
3501         if (fproc != NULL)
3502                 (*fproc)(temp.data);
3503 }
3504 
3505 
3506 static int Cache_Delete (Cache *cache, long id)
3507 {
3508         int index;
3509         ASSERT(Cache_ISVALID(cache));
3510         if (Cache_FindPage(cache,id,&index) != NULL)
3511         {
3512                 Cache_Delete_ByIndex(cache,index);
3513                 return TRUE;
3514         }
3515         return FALSE;
3516 }
3517 
3518 
3519 static int Cache_DeleteLRU (Cache *cache)
3520 {
3521         int i;
3522         for (i=cache->page_count-1; i>=0; --i)
3523         {
3524                 if (cache->page[i].lock ==0)
3525                 {
3526                         Cache_Delete_ByIndex(cache,i);
3527                         return TRUE;
3528                 }
3529         }
3530         return FALSE;
3531 }
3532 
3533 
3534 static void* Cache_Lock (Cache *cache, long id)
3535 {
3536         CachePage *page;
3537         
3538         ASSERT(Cache_ISVALID(cache));
3539         if ((page = Cache_FindPage(cache,id,NULL)) != NULL)
3540         {
3541                 page->lock++;
3542                 cache->hits++;
3543                 return page->data;
3544         }
3545         cache->misses++;
3546         return NULL;
3547 }
3548 
3549 static int Cache_Unlock (Cache *cache, long id)
3550 {
3551         CachePage *page;
3552         int index;
3553         
3554         ASSERT(Cache_ISVALID(cache));
3555         if ((page = Cache_FindPage(cache,id,&index)) != NULL)
3556         {
3557                 if (page->lock > 0)
3558                 {       
3559                         page->lock--;
3560                         if (index > 0)
3561                                 Cache_Touch_ByIndex(cache,index);
3562                         return TRUE;
3563                 }
3564                 ErrPostEx(SEV_INFO,0,0,"Cache_Unlock;  Page(%ld) was not locked",id);
3565         }
3566         return FALSE;
3567 }
3568 
3569 static int Cache_Touch (Cache *cache, long id)
3570 {
3571         int index;
3572         ASSERT(Cache_ISVALID(cache));
3573         if (Cache_FindPage(cache,id,&index) != NULL)
3574         {
3575                 Cache_Touch_ByIndex(cache,index);
3576                 return TRUE;
3577         }
3578         return FALSE;
3579 }
3580 
3581 static void Cache_Touch_ByIndex (Cache *cache, int index)
3582 {
3583         CachePage temp = cache->page[index];
3584         int i;
3585         for (i=index; i>0; --i)
3586         {
3587                 cache->page[i] = cache->page[i-1];
3588         }
3589         cache->page[0] = temp;
3590 }
3591 
3592 static void* Cache_Peek (Cache *cache, long id)
3593 {
3594         CachePage *page;
3595         
3596         ASSERT(Cache_ISVALID(cache));
3597         if ((page = Cache_FindPage(cache,id,NULL)) != NULL)
3598         {
3599                 return page->data;
3600         }
3601         return NULL;
3602 }
3603 
3604 
3605 static void Cache_Purge (Cache *cache)
3606 {
3607         ASSERT(Cache_ISVALID(cache));
3608         
3609         while (Cache_DeleteLRU(cache))
3610                 /* empty statement */ ;
3611 }
3612 
3613 
3614 /**** get rid of this function!  use Cache_ReportStats instead ****/
3615 static void  Cache_LogStats (Cache *cache, const char *name)
3616 {
3617         long total;
3618         int pct1, pct2;
3619         
3620         ASSERT(Cache_ISVALID(cache));
3621         total = cache->hits + cache->misses;
3622         pct1 = (total==0) ? 0 : (int)((cache->hits*100L)/total);
3623         pct2 = (total==0) ? 0 : (int)((cache->misses*100L)/total);
3624         
3625         VERBOSE("\n   Cache Statistics: %s\n",name);
3626         VERBOSE("      %d slots, %d of them currently occupied\n",
3627                                 cache->page_slots, cache->page_count);
3628         VERBOSE("      %ld hits   (%d%%)\n",cache->hits,pct1);
3629         VERBOSE("      %ld misses (%d%%)\n",cache->misses,pct2);
3630         VERBOSE("      %ld total access attempts\n", total);
3631 }
3632 
3633 
3634 #define LONGDIV(x,y) (long)((0.5 + (double)(x)/(double)(y)))
3635 #define PERCENT(x,y) ((y)==0) ? 0 : (int)LONGDIV((x)*100L,(y))
3636 
3637 static char * Cache_ReportStats (Cache *cache, char *buffer)
3638 {
3639         char *p = buffer;
3640         
3641         ASSERT(buffer != NULL);
3642         
3643         if (Cache_ISVALID(cache))
3644         {
3645                 long total = cache->hits + cache->misses;
3646                 if (cache->page_size != 0)
3647                 {
3648                         int kbytes_total, kbytes_used;
3649                         kbytes_total = (int) LONGDIV(cache->page_size*cache->page_slots, KBYTE);
3650                         sprintf(p=strchr(p,0),"   cache memory:   %5d K\n", kbytes_total);
3651                         kbytes_used = (int) LONGDIV(cache->page_size*cache->page_count, KBYTE);
3652                         sprintf(p=strchr(p,0),"   memory in use:  %5d K (%d%%)\n", kbytes_used,
3653                                                 PERCENT(kbytes_used,kbytes_total));
3654                 }
3655                 sprintf(p=strchr(p,0),"   cache hits:     %5ld   (%d%%)\n",
3656                                         cache->hits, PERCENT(cache->hits,total));
3657                 sprintf(p=strchr(p,0),"   cache misses:   %5ld   (%d%%)\n",
3658                                         cache->misses, PERCENT(cache->misses,total));
3659                 sprintf(p=strchr(p,0),"   total attempts: %5ld\n",total);
3660         }
3661         return strchr(p,0);
3662 }
3663 
3664 
3665 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
3666 /* Error reporting functions                                                    */
3667 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
3668 
3669 static int InvalidConfiguration (int code)
3670 {
3671 #ifdef _OLD_CdEntrez_
3672         ErrSev sev = SEV_INFO;
3673 #else
3674         ErrSev sev = SEV_ERROR;
3675 #endif
3676         ErrPostEx(sev,ERR_ConfigFile,code,"CdEntrez module is not configured correctly.  "
3677                                 "Please run EntrezCf to correct the problem.");
3678         return FALSE;
3679 }
3680 
3681 static int FileOutOfDate (const char *fname)
3682 {
3683         ErrPostEx(SEV_ERROR,ERR_BadFile,SUB_BadVersion,
3684                                 "The file %s does not come from the expected "
3685                                 "Entrez version (%d.%d).",fname,_rel_major,_rel_minor);
3686         return FALSE;
3687 }
3688 
3689 static int FileCorrupt (const char *fname)
3690 {
3691         ErrPostEx(SEV_ERROR,ERR_BadFile,SUB_Corrupt,
3692                                 "The file %s appears to be corrupted",fname);
3693         return FALSE;
3694 }
3695 
3696 static int FileNotRecognized (const char *fname)
3697 {
3698         ErrPostEx(SEV_ERROR,ERR_BadFile,SUB_NeedUpdate,
3699                                 "The file %s cannot be read by this release of "
3700                                 "the software.  Please obtain a newer version.", fname);
3701         return FALSE;
3702 }
3703 
3704 static int CatastrophicFailure (int code)
3705 {
3706         ErrPostEx(SEV_ERROR,ERR_DeepDooDoo,code,
3707                                 "Catastrophic Failure in CdEntrez module");
3708         return FALSE;
3709 }
3710 
3711 
3712 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
3713 /* Misc. Utility Functions.   Schuler 05-16-94                                 */
3714 /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
3715 
3716 static char * _GetAppParamStr (const char *filebase, const char *section, 
3717                                 const char *key, const char *dflt)
3718 {
3719         char buffer[256];
3720         GetAppParam((char*)filebase,(char*)section,(char*)key,
3721                                         (char*)dflt,buffer,sizeof buffer);
3722         return buffer[0] ? StrSave(buffer) : NULL;
3723 }
3724 
3725 
3726 static int FileReadSwapShort (unsigned short *buffer, int count, FILE *fd)
3727 {
3728         int n;
3729         if (sizeof(short) != 2)
3730         {
3731                 unsigned short *ptr, val;
3732                 int i, c;
3733         
3734                 for (ptr=buffer, n=0; n<count; ++n)
3735                 {
3736                         for (i=0, val=0; i<2; ++i)
3737                         {
3738                                 if ((c = fgetc(fd)) ==EOF)  
3739                                         break;
3740                                 val <<= 8;
3741                                 val |= (unsigned short)c;
3742                         }
3743                         if (i<2)
3744                                 break;
3745                         *ptr++ = val;
3746                 }
3747         }
3748         else
3749         {
3750                 n = FileRead((void*)buffer,sizeof(short),count,fd);
3751                 SwapUint2Buff(buffer,n);
3752         }
3753         return n;
3754 }
3755 
3756 
3757 static int FileReadSwapLong (unsigned long *buffer, int count, FILE *fd)
3758 {
3759         int n;
3760         if (sizeof(long) != 4)
3761         {
3762                 unsigned long *ptr, val;
3763                 int i, c;
3764         
3765                 for (ptr=buffer, n=0; n<count; ++n)
3766                 {
3767                         for (i=0, val=0; i<4; ++i)
3768                         {
3769                                 if ((c = fgetc(fd)) ==EOF)  
3770                                         break;
3771                                 val <<= 8;
3772                                 val |= (unsigned long)c;
3773                         }
3774                         if (i<4)
3775                                 break;
3776                         *ptr++ = val;
3777                 }
3778         }
3779         else
3780         {
3781                 n = FileRead((void*)buffer,sizeof(long),count,fd);
3782                 SwapLongBuff(buffer,n);
3783         }
3784         return n;
3785 }
3786 
3787 
3788 static int FileReadSwapInt4 (Uint4Ptr buffer, int count, FILE *fd)
3789 {
3790         int n;
3791         if (sizeof(Uint4) != 4)
3792         {
3793                 Uint4 *ptr, val;
3794                 int i, c;
3795         
3796                 for (ptr=buffer, n=0; n<count; ++n)
3797                 {
3798                         for (i=0, val=0; i<4; ++i)
3799                         {
3800                                 if ((c = fgetc(fd)) ==EOF)  
3801                                         break;
3802                                 val <<= 8;
3803                                 val |= (Uint4)c;
3804                         }
3805                         if (i<4)
3806                                 break;
3807                         *ptr++ = val;
3808                 }
3809         }
3810         else
3811         {
3812                 n = FileRead((void*)buffer,sizeof(Uint4),count,fd);
3813                 SwapUint4Buff(buffer,n);
3814         }
3815         return n;
3816 }
3817 
3818 
3819 static char * FileReadStr (FILE *fd, int lbyte)
3820 {
3821         unsigned short len;
3822         
3823         if (lbyte == 2)
3824                 FileReadSwapShort(&len,1,fd);
3825         else
3826                 len = (unsigned short) fgetc(fd);
3827         
3828         if (len > 0)
3829         {
3830                 char *str = MemGet(1+len,MGET_ERRPOST);
3831                 if (str != NULL)
3832                 {
3833                         if (fread(str,1,len,fd) != (size_t)len)
3834                         {
3835                                 MemFree((void*)str);
3836                                 ErrPostEx(SEV_INFO,0,0,"Unexpected EOF");
3837                         }
3838                         else
3839                         {
3840                                 *(str+len) = '\0';
3841                                 return str;
3842                         }
3843                 }
3844         }
3845         return NULL;
3846 }
3847 
3848 
3849 #endif 
3850 
3851 

source navigation ]   [ diff markup ]   [ identifier search ]   [ freetext search ]   [ file search ]  

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.