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