/* 
VALX linker
Copyright 1997-2011 David Lindauer.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

Original 16-bit linker written by:
David Troendle

You may contact the author of this derivative at:
	mailto::camille@bluegrass.net
 */
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <dos.h>
#include <time.h>

#include "langext.h"
#include "defines.h"
#include "types.h"
#include "subs.h"
#include "globals.h"
/*                                 LIBRARY.C                               */

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                            process_libraries                            |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
void process_libraries()
BeginDeclarations
file_info_ptr                          current_lib_file_info;
EndDeclarations
BeginCode
 library_processing_start_time = Now;
 While library_request_count Exceeds 0
  BeginWhile
   TraverseList(lib_file_list, current_lib_file_info)
    BeginTraverse
     process_library(current_lib_file_info);
    EndTraverse;
  EndWhile;
 return;
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                            process_library                              |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
void process_library(file_info_ptr lib_file)
BeginDeclarations
#define Lib_file                       (*lib_file)
bit_32                                 pos;
public_entry_ptr                       pub;
#define Pub                            (*pub)
EndDeclarations
BeginCode
 While Lib_file.request_count Exceeds 0
  BeginWhile
   file_open_for_read(lib_file);
   Lib_file.pass_count++;
   TraverseList(Lib_file.external_list, pub)
    BeginTraverse
     LoopIf((Pub.type_entry IsNot public_in_library) OrIf \
            (Not Pub.Library.requested));
     pos = Bit_32(Pub.Library.page) * Bit_32(Lib_file.page_size);
     file_position(pos);
     Lib_file.module_count++;
     obj_tmodule();
     If Pub.type_entry IsNot internal
      Then
       linker_error(12, "Library directory error:\n"
                        "\tLibrary:  \"%s\"\n"
                        "\t Module:  \"%s\"\n"
                        "\t Offset:  %lu\n"
                        "\t  Error:  Above module did not resolve \"%s\"\n"
                        "\t          when the library directory indicated it "
                                     "would.\n",
                        Lib_file.filename,
                        (*tmodule_name).symbol,
                        pos,
                        Pub.symbol);
      EndIf;
    EndTraverse;
   file_close_for_read();
  EndWhile;
 return;
EndCode
#undef Lib_file
#undef Pub

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                       process_library_directories                       |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
void process_library_directories()
BeginDeclarations
file_type                              save;
file_info_ptr                          current_lib_file_info;
EndDeclarations
BeginCode
 save = infile;
 infile.buffer = infile_buffer_2;
 library_directory_start_time = Now;
 public_sort_array = (public_entry_ptr_array)
                       allocate_memory(
                        Bit_32(sizeof(public_entry_ptr)) *
                         Bit_32(MAX_PUBLICS_IN_LIBRARY));
 TraverseList(lib_file_new_list, current_lib_file_info)
  BeginTraverse
   process_library_directory(current_lib_file_info);
  EndTraverse;
 Pop lib_file_new_list InTo current_lib_file_info EndPop;
 While (current_lib_file_info)
  BeginWhile
   Insert current_lib_file_info AtEnd InList lib_file_list EndInsert;
   Pop lib_file_new_list InTo current_lib_file_info EndPop;
  EndWhile;
  
 release_memory(public_sort_array);
 infile = save;
 return;
EndCode

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                        process_library_directory                        |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
void process_library_directory(file_info_ptr lib_file)
BeginDeclarations
#define Lib_file                       (*lib_file)
bit_16                                 entry_number;
bit_16                                 current_block;
library_directory_ptr                  directory_page;
#define Directory_page                 (*directory_page)
library_file_header_ptr                lib_hdr;
#define Lib_hdr                        (*lib_hdr)
bit_16                                 n_directory_blocks;
public_entry_ptr                       pub;
#define Pub                            (*pub)
public_entry_ptr_array                 public_array;
library_symbol_entry_ptr               symbol;
bit_16                                 symbol_count;
bit_16                                 symbol_index;
#define Symbol                         (*symbol)
bit_16                                *symbol_in_page;
#define Symbol_in_page                 (*symbol_in_page)
EndDeclarations
BeginCode
 lib_hdr = (library_file_header_ptr) object_file_element;
 file_open_for_read(lib_file);
 file_IO_limit(sizeof(library_file_header_type));
 file_read(object_file_element, sizeof(library_file_header_type));
 If Lib_hdr.flag IsNot 0xF0
  Then
   linker_error(-1,"File \"%s\" is not in LIB file format and is being ignored.\n",
                  (*lib_file).filename);
   Delete lib_file FromList lib_file_list EndDelete;
   file_close_for_read();
   return;
  EndIf;
 Lib_file.page_size            = Lib_hdr.page_size + 3;
 Lib_file.request_count        = 0;
 First(Lib_file.external_list) =
 Last(Lib_file.external_list)  = Null;
 n_directory_blocks            = Lib_hdr.n_directory_blocks;
 file_position(Lib_hdr.directory_position);
 file_IO_limit(0);
 symbol_count = 0;
 public_array = public_sort_array;
 For current_block=0;
     current_block LessThan  n_directory_blocks;
     current_block++
  BeginFor
   file_read(object_file_element, 512);
   directory_page = (library_directory_ptr) object_file_element;
   For entry_number=0; entry_number LessThan 37; entry_number++
    BeginFor
     symbol_index =
      Directory_page.offset_to_symbol[entry_number] ShiftedLeft 1;
     LoopIf(symbol_index IsZero);
     symbol = (library_symbol_entry_ptr)
              Addr(Directory_page.offset_to_symbol[symbol_index]);
     symbol_in_page = (bit_16 *)
                      Addr(Symbol.symbol[Symbol.length_of_symbol]);
     If case_ignore.val
      Then
       far_to_lower(Symbol.symbol, Symbol.length_of_symbol);
      EndIf;
     pub = lookup_public(Symbol.length_of_symbol, Symbol.symbol, 0);
/*+-------------------------------------------------------------------------+
  |                                                                         |
  | The Microsoft linker does not seem to concern itself with the ambiguity |
  | which occurs when more than one library contains a module resolving an  |
  | external reference.  It just seems to take the module from the first    |
  | library which can resolve the external.  This linker behaves in the     |
  | same manner except it will log when it ignores a module in a library.   |
  |                                                                         |
  +-------------------------------------------------------------------------+*/
     If Pub.type_entry IsNot unused
      Then  /* Log the ambiguity. */
#ifdef XXXXX
       linker_error(4,"The symbol \"%s\" is defined in the libraries\n"
                      "\t\"%s\" and \"%s\".\n"
                      "\tThe latter has been ignored.\n",
                      unmangle(Pub.symbol),
                      (*Pub.Library.lib_file).filename, Lib_file.filename);
#endif
      Else
       Pub.type_entry        = public_in_library;
       Pub.Library.page      = Symbol_in_page;
       Pub.Library.lib_file  = lib_file;
       Pub.Library.requested = False;
       If symbol_count NotLessThan MAX_PUBLICS_IN_LIBRARY
        Then
         linker_error(8,"Limitation of not more than %u public symbols in "
                        "a library\n"
                        "\texceeded for library \"%s\".\n",
                        MAX_PUBLICS_IN_LIBRARY,
                        Lib_file.filename);
        EndIf;
       *public_array++ = pub;
       symbol_count++;
      EndIf;
    EndFor;
  EndFor;
 file_close_for_read();
 If symbol_count Exceeds 0
  Then
   sort_directory(0, symbol_count-1);
   public_array = public_sort_array;
   For entry_number=0; entry_number LessThan symbol_count; entry_number++
    BeginFor
     Insert *public_array++ AtEnd InList Lib_file.external_list EndInsert;
    EndFor;
  EndIf;
 return;
EndCode
#undef Pub
#undef Lib_file
#undef Lib_hdr
#undef Library_page
#undef Symbol
#undef Symbol_in_page

/*+-------------------------------------------------------------------------+
  |                                                                         |
  |                              sort_directory                             |
  |                                                                         |
  +-------------------------------------------------------------------------+

Well, there is always much ado concerning the best sort method for a
particular application.  What is used here is a simple "quick sort".
See Knuth, "Searching and Sorting", page 114 for a description of the
algorithm.  What is really important here is why we are sorting.  After
the OBJ modules are processed, we must scan the libraries to see what
modules from the library must be included.  If the symbols in the
symbol table are in ascending module order, then we will include modules
from the library in ascending order.  This may not seem too important
at first glance, but consider that the library will be heavily buffered
and we can reduce I/O overhead by processing modules in the same buffer.
We may not get all the modules in one pass (a module at the end of the
library could require a module at the beginning of the library).  However,
experience has shown that all modules from a library can usually be
included in less than three passes.  The linker statistics will show
the number of passes for and modules included from each library.  If
you want to test this theory, just comment out the sort in the
procedure "process_library_directory" and compare the number of passes
before and after.

Now, all this could be avoided if the librarian had placed the modules
in the library in some sort of an approximation of a topologically
sorted order.  (A true topological sort is not usually possible because
relational loops are not all that infrequent.)  However, if modules with
relational loops were placed close to each other, it is very likely
that the I/O buffering could cause the library to be fully processed in
one pass.  This would improve performance, but it would not insure that
a library could be processed in exactly one pass.

Consider the case where a relational loop occurs with modules which are
not in the same library (this case forces multiple passes).  E.G.,
suppose module A1 in library A requires module B1 in library B.  Further,
suppose module B1 in turn requires module A2 back in library A.  When
processing library A (the first time), we have no way of knowing we also
need A2 until after we process B1 in library B.  We then have to go back
to library A and pick up A2.  The more this happens, the longer the link
time.*/
void sort_directory(bit_16 left, bit_16 right)
BeginDeclarations
bit_16                                 i;
bit_16                                 j;
public_entry_ptr                       temp;
EndDeclarations
BeginCode
 If left NotLessThan right
  Then
   return;
  EndIf;
 i = left;
 j = right;
 While i LessThan j
  BeginWhile
   While i LessThan j
    BeginWhile
     If (*public_sort_array[i]).Library.page GreaterThan
        (*public_sort_array[j]).Library.page
      Then
       temp                 = public_sort_array[i];
       public_sort_array[i] = public_sort_array[j];
       public_sort_array[j] = temp;
       ExitLoop;
      EndIf;
     j--;
    EndWhile;
   While i LessThan j
    BeginWhile
     If (*public_sort_array[i]).Library.page GreaterThan
        (*public_sort_array[j]).Library.page
      Then
       temp                 = public_sort_array[i];
       public_sort_array[i] = public_sort_array[j];
       public_sort_array[j] = temp;
       ExitLoop;
      EndIf;
     i++;
    EndWhile;
  EndWhile;
  If i Exceeds 0
   Then
    sort_directory(left, i-1);
   EndIf;
 sort_directory(i+1,  right);
 return;
EndCode
