219e7e339083658fe2704f5e399922fdfbea3107
[fw/sdcc] / as / link / lklibr.c
1 /* lklibr.c
2
3    Copyright (C) 1989-1995 Alan R. Baldwin
4    721 Berkeley St., Kent, Ohio 44240
5    Copyright (C) 2008-2009 Borut Razem, borut dot razem at siol dot net
6
7 This program is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20
21 /*
22  * With contributions for the
23  * object libraries from
24  * Ken Hornstein
25  * kenh@cmf.nrl.navy.mil
26  *
27  */
28
29 /*
30  * Extensions: P. Felber
31  */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <assert.h>
38
39 #include "aslink.h"
40 #include "lkrel.h"
41 #include "lklibr.h"
42
43 /*)Module   lklibr.c
44  *
45  *  The module lklibr.c contains the functions which
46  *  (1) specify the path(s) to library files [.LIB]
47  *  (2) specify the library file(s) [.LIB] to search
48  *  (3) search the library files for specific symbols
49  *      and link the module containing this symbol
50  *
51  *  lklibr.c contains the following functions:
52  *      VOID    addpath()
53  *      VOID    addlib()
54  *      VOID    addfile()
55  *      VOID    search()
56  *      VOID    fndsym()
57  *      VOID    library()
58  *      VOID    loadfile()
59  *
60  */
61
62 #define EQ(A,B) !strcmp((A),(B))
63 #define NELEM(x) (sizeof (x) / sizeof (*x))
64
65 #ifdef INDEXLIB
66 /* First entry in the library object symbol cache */
67 pmlibraryfile libr = NULL;
68
69 int buildlibraryindex (void);
70 void freelibraryindex (void);
71 #endif /* INDEXLIB */
72
73 struct aslib_target *aslib_targets[] = {
74   &aslib_target_sdcclib,
75   &aslib_target_ar,
76   &aslib_target_lib,
77 };
78
79 /*)Function VOID    addpath()
80  *
81  *  The function addpath() creates a linked structure containing
82  *  the paths to various object module library files.
83  *
84  *  local variables:
85  *      lbpath  *lbph       pointer to new path structure
86  *      lbpath  *lbp        temporary pointer
87  *
88  *  global variables:
89  *      lbpath  *lbphead    The pointer to the first
90  *                          path structure
91  *
92  *   functions called:
93  *      char    getnb()     lklex.c
94  *      VOID *  new()       lksym.c
95  *      int     strlen()    c_library
96  *      char *  strcpy()    c_library
97  *      VOID    unget()     lklex.c
98  *
99  *  side effects:
100  *      An lbpath structure may be created.
101  */
102
103 VOID
104 addpath (void)
105 {
106   struct lbpath *lbph, *lbp;
107
108   lbph = (struct lbpath *) new (sizeof (struct lbpath));
109   if (lbphead == NULL)
110     {
111       lbphead = lbph;
112     }
113   else
114     {
115       lbp = lbphead;
116       while (lbp->next)
117         {
118           lbp = lbp->next;
119         }
120       lbp->next = lbph;
121     }
122   unget (getnb ());
123   lbph->path = strdup (ip);
124 }
125
126 /*)Function VOID    addlib()
127  *
128  *  The function addlib() tests for the existance of a
129  *  library path structure to determine the method of
130  *  adding this library file to the library search structure.
131  *
132  *  This function calls the function addfile() to actually
133  *  add the library file to the search list.
134  *
135  *  local variables:
136  *      lbpath  *lbph       pointer to path structure
137  *
138  *  global variables:
139  *      lbpath  *lbphead    The pointer to the first
140  *                          path structure
141  *      ip a pointer to the library name
142  *
143  *   functions called:
144  *      VOID    addfile()   lklibr.c
145  *      char    getnb()     lklex.c
146  *      VOID    unget()     lklex.c
147  *
148  *  side effects:
149  *      The function addfile() may add the file to
150  *      the library search list.
151  */
152
153 VOID
154 addlib (void)
155 {
156   struct lbpath *lbph;
157   int foundcount = 0;
158
159   unget (getnb ());
160
161   if (lbphead == NULL)
162     {
163       foundcount = addfile (NULL, ip);
164     }
165   else
166     {
167       for (lbph = lbphead; lbph; lbph = lbph->next)
168         {
169           foundcount += addfile (lbph->path, ip);
170         }
171     }
172   if (foundcount == 0)
173     {
174       fprintf (stderr, "?ASlink-Warning-Couldn't find library '%s'\n", ip);
175     }
176 }
177
178 /*)Function int addfile(path,libfil)
179  *
180  *      char    *path       library path specification
181  *      char    *libfil     library file specification
182  *
183  *  The function addfile() searches for the library file
184  *  by concatenating the path and libfil specifications.
185  *  if the library is found, an lbname structure is created
186  *  and linked to any previously defined structures.  This
187  *  linked list is used by the function fndsym() to attempt
188  *  to find any undefined symbols.
189  *
190  *  The function does not give report an error on invalid
191  *  path / file specifications or if the file is not found.
192  *
193  *  local variables:
194  *      lbname  *lbnh       pointer to new name structure
195  *      lbname  *lbn        temporary pointer
196  *
197  *  global variables:
198  *      lbname  *lbnhead    The pointer to the first
199  *                          path structure
200  *
201  *   functions called:
202  *      char    getnb()     lklex.c
203  *      VOID *  new()       lksym.c
204  *      int     strlen()    c_library
205  *      char *  strcpy()    c_library
206  *      VOID    unget()     lklex.c
207  *
208  *  side effects:
209  *      An lbname structure may be created.
210  *
211  *  return:
212  *      1: the library was found
213  *      0: the library was not found
214  */
215
216 int
217 addfile (char *path, char *libfil)
218 {
219   FILE *fp;
220   char *str;
221   struct lbname *lbnh, *lbn;
222 #ifdef  OTHERSYSTEM
223   int libfilinc = 0;
224 #endif
225
226   if (path != NULL)
227     {
228       str = (char *) new (strlen (path) + strlen (libfil) + 6);
229       strcpy (str, path);
230 #ifdef  OTHERSYSTEM
231       if (strlen (str) && (str[strlen (str) - 1] != '/') && (str[strlen (str) - 1] != LKDIRSEP))
232         {
233           strcat (str, LKDIRSEPSTR);
234         }
235 #endif
236     }
237   else
238     {
239       str = (char *) new (strlen (libfil) + 5);
240     }
241
242 #ifdef  OTHERSYSTEM
243   if ((libfil[0] == '/') || (libfil[0] == LKDIRSEP))
244     {
245       libfil++;
246       libfilinc = 1;
247     }
248 #endif
249
250   strcat (str, libfil);
251   if (strchr (libfil, FSEPX) == NULL)
252     {
253       sprintf (&str[strlen (str)], "%clib", FSEPX);
254     }
255
256   fp = fopen (str, "rb");
257   if (fp == NULL)
258     {
259       /*Ok, that didn't work.  Try with the 'libfil' name only */
260 #ifdef  OTHERSYSTEM
261       if (libfilinc)
262         libfil--;
263 #endif
264       fp = fopen (libfil, "rb");
265       if (fp != NULL)
266         {
267           /*Bingo!  'libfil' is the absolute path of the library */
268           strcpy (str, libfil);
269           path = NULL;          /*This way 'libfil' and 'path' will be rebuilt from 'str' */
270         }
271     }
272
273   if (path == NULL)
274     {
275       /*'path' can not be null since it is needed to find the object files associated with
276          the library.  So, get 'path' from 'str' and then chop it off and recreate 'libfil'.
277          That way putting 'path' and 'libfil' together will result into the original filepath
278          as contained in 'str'. */
279       int j;
280       path = strdup (str);
281       for (j = strlen (path) - 1; j >= 0; j--)
282         {
283           if ((path[j] == '/') || (path[j] == LKDIRSEP))
284             {
285               strcpy (libfil, &path[j + 1]);
286               path[j + 1] = 0;
287               break;
288             }
289         }
290       if (j <= 0)
291         path[0] = 0;
292     }
293
294   if (fp != NULL)
295     {
296       fclose (fp);
297       lbnh = (struct lbname *) new (sizeof (struct lbname));
298       if (lbnhead == NULL)
299         {
300           lbnhead = lbnh;
301         }
302       else
303         {
304           lbn = lbnhead;
305           while (lbn->next)
306             {
307               lbn = lbn->next;
308             }
309           lbn->next = lbnh;
310         }
311
312       lbnh->path = path;
313       lbnh->libfil = strdup (libfil);
314       lbnh->libspc = str;
315       return 1;
316     }
317   else
318     {
319       free (str);
320       return 0;
321     }
322 }
323
324 /*)Function VOID    search()
325  *
326  *  The function search() looks through all the symbol tables
327  *  at the end of pass 1.  If any undefined symbols are found
328  *  then the function fndsym() is called. Function fndsym()
329  *  searches any specified library files to automagically
330  *  import the object modules containing the needed symbol.
331  *
332  *  After a symbol is found and imported by the function
333  *  fndsym() the symbol tables are again searched.  The
334  *  symbol tables are search until no more symbols can be
335  *  resolved within the library files.  This ensures that
336  *  back references from one library module to another are
337  *  also resolved.
338  *
339  *  local variables:
340  *      int     i           temporary counter
341  *      sym     *sp         pointer to a symbol structure
342  *      int     symfnd      found a symbol flag
343  *
344  *  global variables:
345  *      sym     *symhash[]  array of pointers to symbol tables
346  *
347  *   functions called:
348  *      int     fndsym()    lklibr.c
349  *
350  *  side effects:
351  *      If a symbol is found then the library object module
352  *      containing the symbol will be imported and linked.
353  */
354
355 VOID
356 search (void)
357 {
358   register struct sym *sp;
359   register int i, symfnd;
360
361   /*
362    * Look for undefined symbols.  Keep
363    * searching until no more symbols are resolved.
364    */
365   symfnd = 1;
366   while (symfnd)
367     {
368       symfnd = 0;
369       /*
370        * Look through all the symbols
371        */
372       for (i = 0; i < NHASH; ++i)
373         {
374           sp = symhash[i];
375           while (sp)
376             {
377               /* If we find an undefined symbol
378                * (one where S_DEF is not set), then
379                * try looking for it.  If we find it
380                * in any of the libraries then
381                * increment symfnd.  This will force
382                * another pass of symbol searching and
383                * make sure that back references work.
384                */
385               if ((sp->s_type & S_DEF) == 0)
386                 {
387                   if (fndsym (sp->s_id))
388                     {
389                       symfnd++;
390                     }
391                 }
392               sp = sp->s_sp;
393             }
394         }
395     }
396 }
397
398 /*)Function VOID    fndsym(name)
399  *
400  *      char    *name       symbol name to find
401  *
402  *  The function fndsym() searches through all combinations of the
403  *  library path specifications (input by the -k option) and the
404  *  library file specifications (input by the -l option) that
405  *  lead to an existing file.
406  *
407  *  The file specicifation may be formed in one of two ways:
408  *
409  *  (1) If the library file contained an absolute
410  *      path/file specification then this becomes filspc.
411  *      (i.e. C:\...)
412  *
413  *  (2) If the library file contains a relative path/file
414  *      specification then the concatenation of the path
415  *      and this file specification becomes filspc.
416  *      (i.e. \...)
417  *
418  *  The structure lbfile is created for the first library
419  *  object file which contains the definition for the
420  *  specified undefined symbol.
421  *
422  *  If the library file [.LIB] contains file specifications for
423  *  non existant files, no errors are returned.
424  *
425  *  local variables:
426  *      char    buf[]       [.REL] file input line
427  *      char    c           [.REL] file input character
428  *      FILE    *fp         file handle for object file
429  *      lbfile  *lbf        temporary pointer
430  *      lbfile  *lbfh       pointer to lbfile structure
431  *      FILE    *libfp      file handle for library file
432  *      lbname  *lbnh       pointer to lbname structure
433  *      char    *path       file specification path
434  *      char    relfil[]    [.REL] file specification
435  *      char    *str        combined path and file specification
436  *      char    symname[]   [.REL] file symbol string
437  *
438  *  global variables:
439  *      lbname  *lbnhead    The pointer to the first
440  *                          name structure
441  *      lbfile  *lbfhead    The pointer to the first
442  *                          file structure
443  *
444  *   functions called:
445  *      int     fclose()    c_library
446  *      FILE    *fopen()    c_library
447  *      VOID    free()      c_library
448  *      char    getnb()     lklex.c
449  *      VOID    lkexit()    lkmain.c
450  *      VOID    loadfile()  lklibr.c
451  *      VOID *  new()       lksym.c
452  *      char *  sprintf()   c_library
453  *      int     sscanf()    c_library
454  *      char *  strcat()    c_library
455  *      char *  strchr()    c_library
456  *      char *  strcpy()    c_library
457  *      int     strlen()    c_library
458  *      int     strncmp()   c_library
459  *      VOID    unget()     lklex.c
460  *
461  *  side effects:
462  *      If the symbol is found then a new lbfile structure
463  *      is created and added to the linked list of lbfile
464  *      structures.  The file containing the found symbol
465  *      is linked.
466  */
467
468 #ifdef INDEXLIB
469 int
470 fndsym (char *name)
471 {
472   struct lbfile *lbfh, *lbf;
473   pmlibraryfile ThisLibr;
474   pmlibrarysymbol ThisSym = NULL;
475
476   pmlibraryfile FirstFound;
477   int numfound = 0;
478
479   D ("Searching symbol: %s\n", name);
480
481   /* Build the index if this is the first call to fndsym */
482   if (libr == NULL)
483     buildlibraryindex ();
484
485   /* Iterate through all library object files */
486   FirstFound = libr;            /* So gcc stops whining */
487   for (ThisLibr = libr; ThisLibr != NULL; ThisLibr = ThisLibr->next)
488     {
489       /* Iterate through all symbols in an object file */
490       for (ThisSym = ThisLibr->symbols; ThisSym != NULL; ThisSym = ThisSym->next)
491         {
492           if (!strcmp (ThisSym->name, name))
493             {
494               if ((!ThisLibr->loaded) && (numfound == 0))
495                 {
496                   /* Object file is not loaded - add it to the list */
497                   lbfh = (struct lbfile *) new (sizeof (struct lbfile));
498                   if (lbfhead == NULL)
499                     {
500                       lbfhead = lbfh;
501                     }
502                   else
503                     {
504                       for (lbf = lbfhead; lbf->next != NULL; lbf = lbf->next)
505                         ;
506
507                       lbf->next = lbfh;
508                     }
509                   lbfh->libspc = ThisLibr->libspc;
510                   lbfh->filspc = ThisLibr->filspc;
511                   lbfh->relfil = strdup (ThisLibr->relfil);
512                   lbfh->offset = ThisLibr->offset;
513                   lbfh->type = ThisLibr->type;
514
515                   (*aslib_targets[lbfh->type]->loadfile) (lbfh);
516
517                   ThisLibr->loaded = 1;
518                 }
519
520               if (numfound == 0)
521                 {
522                   numfound++;
523                   FirstFound = ThisLibr;
524                 }
525               else
526                 {
527                   char absPath1[PATH_MAX];
528                   char absPath2[PATH_MAX];
529 #if defined(__BORLANDC__) || defined(_MSC_VER) || defined(__MINGW32__)
530                   int j;
531
532                   _fullpath (absPath1, FirstFound->libspc, PATH_MAX);
533                   _fullpath (absPath2, ThisLibr->libspc, PATH_MAX);
534                   for (j = 0; absPath1[j] != 0; j++)
535                     absPath1[j] = tolower ((unsigned char) absPath1[j]);
536                   for (j = 0; absPath2[j] != 0; j++)
537                     absPath2[j] = tolower ((unsigned char) absPath2[j]);
538 #else
539                   realpath (FirstFound->libspc, absPath1);
540                   realpath (ThisLibr->libspc, absPath2);
541 #endif
542                   if (!(EQ (absPath1, absPath2) && EQ (FirstFound->relfil, ThisLibr->relfil)))
543                     {
544                       if (numfound == 1)
545                         {
546                           fprintf (stderr, "?ASlink-Warning-Definition of public symbol '%s'" " found more than once:\n", name);
547                           fprintf (stderr, "   Library: '%s', Module: '%s'\n", FirstFound->libspc, FirstFound->relfil);
548                         }
549                       fprintf (stderr, "   Library: '%s', Module: '%s'\n", ThisLibr->libspc, ThisLibr->relfil);
550                       numfound++;
551                     }
552                 }
553             }
554         }
555     }
556   return numfound;
557 }
558
559 struct add_sym_s
560 {
561   pmlibraryfile plf;
562   pmlibrarysymbol pls;
563 };
564
565 static int
566 add_sybmol (const char *sym, void *param)
567 {
568   struct add_sym_s *as = (struct add_sym_s *) param;
569   pmlibrarysymbol ps = (pmlibrarysymbol) new (sizeof (mlibrarysymbol));
570
571   D ("    Indexing symbol: %s\n", sym);
572
573   as->plf->loaded = 0;
574   ps->next = NULL;
575   ps->name = strdup (sym);
576
577   if (as->pls == NULL)
578     {
579       as->pls = as->plf->symbols = ps;
580     }
581   else
582     {
583       as->pls->next = ps;
584       as->pls = as->pls->next;
585     }
586
587   return 0;
588 }
589
590 pmlibrarysymbol
591 add_rel_index (FILE * fp, long size, pmlibraryfile This)
592 {
593   struct add_sym_s as;
594   as.plf = This;
595   as.pls = This->symbols;
596
597   assert (This->symbols == NULL);
598
599   enum_symbols (fp, size, &add_sybmol, &as);
600
601   return as.pls;
602 }
603
604 /* buildlibraryindex - build an in-memory cache of the symbols contained in
605  *                     the libraries
606  */
607 int
608 buildlibraryindex (void)
609 {
610   pmlibraryfile This = NULL;
611   struct lbname *lbnh;
612
613   /*
614    * Search through every library in the linked list "lbnhead".
615    */
616   for (lbnh = lbnhead; lbnh; lbnh = lbnh->next)
617     {
618       FILE *libfp;
619       int i;
620
621       D ("Indexing library: %s\n", lbnh->libspc);
622
623       if ((libfp = fopen (lbnh->libspc, "rb")) == NULL)
624         {
625           fprintf (stderr, "?ASlink-Error-Cannot open library file %s\n", lbnh->libspc);
626           lkexit (1);
627         }
628
629       for (i = 0; i < NELEM (aslib_targets); ++i)
630         {
631           if ((*aslib_targets[i]->is_lib) (libfp))
632             {
633               This = (*aslib_targets[i]->buildlibraryindex) (lbnh, libfp, This, i);
634               break;
635             }
636         }
637
638       if (i >= NELEM (aslib_targets))
639         fprintf (stderr, "?ASlink-Error-Unknown library file format %s\n", lbnh->libspc);
640
641       fclose (libfp);
642     }
643
644   return 0;
645 }
646
647 /* Release all memory allocated for the in-memory library index */
648 void
649 freelibraryindex (void)
650 {
651   pmlibraryfile ThisLibr, ThisLibr2Free;
652   pmlibrarysymbol ThisSym, ThisSym2Free;
653
654   ThisLibr = libr;
655
656   while (ThisLibr)
657     {
658       ThisSym = ThisLibr->symbols;
659
660       while (ThisSym)
661         {
662           free (ThisSym->name);
663           ThisSym2Free = ThisSym;
664           ThisSym = ThisSym->next;
665           free (ThisSym2Free);
666         }
667       free (ThisLibr->filspc);
668       free (ThisLibr->relfil);
669       ThisLibr2Free = ThisLibr;
670       ThisLibr = ThisLibr->next;
671       free (ThisLibr2Free);
672     }
673
674   libr = NULL;
675 }
676
677 #else /* INDEXLIB */
678
679 struct load_sym_s
680 {
681   const char *name;
682   struct lbname *lbnh;
683   const char *relfil;
684   const char *filspc;
685   int offset;
686   int type;
687 };
688
689 static int
690 load_sybmol (const char *sym, void *params)
691 {
692   struct load_sym_s *ls = (struct load_sym_s *) params;
693
694   D ("    Symbol: %s\n", sym);
695
696   if (strcmp (ls->name, sym) == 0)
697     {
698       struct lbfile *lbfh, *lbf;
699
700       D ("    Symbol %s found in module %s!\n", sym, ls->relfil);
701
702       lbfh = (struct lbfile *) new (sizeof (struct lbfile));
703       lbfh->libspc = ls->lbnh->libspc;
704       lbfh->relfil = strdup (ls->relfil);
705       lbfh->filspc = strdup (ls->filspc);
706       lbfh->offset = ls->offset;
707       lbfh->type = ls->type;
708
709       if (lbfhead == NULL)
710         lbfhead = lbfh;
711       else
712         {
713           for (lbf = lbfhead; lbf->next != NULL; lbf = lbf->next)
714               ;
715           lbf->next = lbfh;
716         }
717       (*aslib_targets[ls->type]->loadfile) (lbfh);
718
719       return 1;
720     }
721   else
722     return 0;
723 }
724
725 /*)Function int is_module_loaded(filspc)
726  *
727  * If this module has been already loaded
728  */
729
730 int
731 is_module_loaded (const char *filspc)
732 {
733   struct lbfile *lbf;
734
735   for (lbf = lbfhead; lbf != NULL; lbf = lbf->next)
736     {
737       if (EQ (filspc, lbf->filspc))
738         {
739           D ("  Module %s already loaded!\n", filspc);
740           return 1;       /* Module already loaded */
741         }
742     }
743   return 0;
744 }
745
746 int
747 add_rel_file (const char *name, struct lbname *lbnh, const char *relfil,
748               const char *filspc, int offset, FILE * fp, long size, int type)
749 {
750   struct load_sym_s ls;
751
752   /* If this module has been loaded already don't load it again. */
753   if (is_module_loaded (filspc))
754     return 0;
755   else
756     {
757       ls.name = name;
758       ls.lbnh = lbnh;
759       ls.relfil = relfil;
760       ls.filspc = filspc;
761       ls.offset = offset;
762       ls.type = type;
763
764       return enum_symbols (fp, size, &load_sybmol, &ls);
765   }
766 }
767
768 int
769 fndsym (const char *name)
770 {
771   FILE *libfp;
772   struct lbname *lbnh;
773   int i;
774
775   /*
776    * Search through every library in the linked list "lbnhead".
777    */
778
779   D ("Searching symbol: %s\n", name);
780
781   for (lbnh = lbnhead; lbnh; lbnh = lbnh->next)
782     {
783       int ret = 0;
784
785       D ("Library: %s\n", lbnh->libspc);
786
787       if ((libfp = fopen (lbnh->libspc, "rb")) == NULL)
788         {
789           fprintf (stderr, "?ASlink-Error-Cannot open library file %s\n", lbnh->libspc);
790           lkexit (1);
791         }
792
793       for (i = 0; i < NELEM (aslib_targets); ++i)
794         {
795           if ((*aslib_targets[i]->is_lib) (libfp))
796             {
797               ret = (*aslib_targets[i]->fndsym) (name, lbnh, libfp, i);
798               break;
799             }
800         }
801
802       if (i >= NELEM (aslib_targets))
803         fprintf (stderr, "?ASlink-Error-Unknown library file format %s\n", lbnh->libspc);
804
805       fclose (libfp);
806
807       if (ret)
808         return 1;
809
810     }                           /* Ends good open of libr file */
811   return 0;
812 }
813 #endif /* INDEXLIB */
814
815 /*)Function VOID    library()
816  *
817  *  The function library() links all the library object files
818  *  contained in the lbfile structures.
819  *
820  *  local variables:
821  *      lbfile  *lbfh       pointer to lbfile structure
822  *
823  *  global variables:
824  *      lbfile  *lbfhead    pointer to first lbfile structure
825  *
826  *   functions called:
827  *      VOID    loadfile    lklibr.c
828  *
829  *  side effects:
830  *      Links all files contained in the lbfile structures.
831  */
832
833 VOID
834 library (void)
835 {
836   struct lbfile *lbfh;
837
838   for (lbfh = lbfhead; lbfh; lbfh = lbfh->next)
839     (*aslib_targets[lbfh->type]->loadfile) (lbfh);
840
841 #ifdef INDEXLIB
842   freelibraryindex ();
843 #endif
844 }