# include <h.stdio>
# include <h.limits>
# include "c.libdef"

# define SYMBOL_ADMIN_SIZE 4

tree_ptr tree ;          /* the tree */
int num_modules, amount_to_add, deleted_offset ;
tree_ptr last_node ;
char *compiler_header = "Beebug C Compiler V1.4\n",
     buffer [32] ;

extern unsigned short get_byte(void) ;
extern void read_symbol(void) ;
extern char filename[] ;
extern int offset, modified ;
extern FILE *fp,*new_fp ;

free_node (tree_ptr cursor)
   {
   free (cursor->name) ;
   free (cursor->filename) ;
   free (cursor) ;
   }

free_tree(void)
   {
   traverse_tree (tree,free_node) ;
   init_tree() ;
   }

no_room(void)
   {
   fprintf (stderr,"Insufficient memory\n") ;
   exit (0) ;
   }

tree_ptr new_node (record *data) 
   {
   tree_ptr node ;
   
   if ((node = malloc (sizeof (struct tree_node))) == NULL)
      no_room() ;
   node->left = node->right = NULL ;
   if ((node->name = malloc (strlen(data->name)+1)) == NULL)
      no_room() ;
   strcpy (node->name,data->name) ;
   node->old_offset = node->new_offset = data->offset ;
   node->deleted = node->new = FALSE ;
   return node ;
   }

tree_ptr insert_right (tree_ptr cursor, record *data)
   {
   return cursor->right = new_node (data) ;
   }

tree_ptr insert_left (tree_ptr cursor, record *data)
   {
   return cursor->left = new_node (data) ;
   }

tree_ptr insert_at_root (record *data)
   {
   return tree = new_node (data) ;
   }

init_tree(void)
   {
   tree = NULL ;
   }

int right_empty (tree_ptr cursor)
   {
   return cursor->right == NULL ;
   }

int left_empty (tree_ptr cursor)
   {
   return cursor->left == NULL ;
   }

tree_ptr insert_tree (tree_ptr cursor, record *data)
   {
   if (tree == NULL)
      return insert_at_root (data) ;
   if (data->offset < cursor->old_offset)
      return left_empty(cursor) ? insert_left(cursor,data)
                                : insert_tree (cursor->left,data) ;
   return right_empty(cursor) ? insert_right(cursor,data)
                              : insert_tree (cursor->right,data) ;
   }

tree_ptr search_tree (tree_ptr cursor, char *name)
   {
   tree_ptr ptr ;

   if (cursor == NULL)
      return NULL ;
   if (!strcmp(cursor->name,name))
      return cursor ;
   if ((ptr=search_tree(cursor->left,name)) != NULL)
       return ptr ;
   return search_tree (cursor->right,name) ;
   }


add_amount (tree_ptr cursor)
   {
   if (!cursor->new)
      cursor->new_offset += amount_to_add ;
   }

calculate_length (tree_ptr cursor)
   {
   if (last_node != NULL)
      last_node->length = cursor->old_offset - last_node->old_offset ;
   last_node = cursor ;
   }

read_directory(void)
   {
   record data ;
   int record_number = 0;
   int last_offset, file_length ;

   putchar ('\n') ;
   amount_to_add = -(int)ftell (fp) ;   /* record start of dir */
   for (;;) 
      {
      if (get_byte() == 0xff)
         break ;
      read_symbol() ;
      printf ("Reading... %d\r",++record_number) ;
      strcpy (data.name,buffer) ;
      data.offset = offset ;
      insert_tree (tree,&data) ;
      }
   putchar ('\n') ;
   last_node = NULL ;
   traverse_tree (tree,calculate_length) ;
   fseek (fp,0,SEEK_END) ;
   file_length = (int)ftell (fp) ;
   last_offset = last_node->old_offset ;
   last_node->length = file_length - last_offset ;
   traverse_tree (tree,add_amount) ;      /* subtract length of header */
   modified = FALSE ;
   }


traverse_tree(tree_ptr cursor,int (*function)() )
   {
   if (cursor == NULL)
      return ;
   traverse_tree (cursor->left,function) ;
   (*function) (cursor) ;
   traverse_tree (cursor->right,function) ;
   }


list_node(tree_ptr cursor)
   {
   printf ("%-20.20s ",cursor->name) ;
   if (cursor->deleted)
      printf ("Deleted\n") ;
   else
      if (cursor->new)
         printf ("New (%s)\n",cursor->filename) ;
   else printf ("%X\n",cursor->length) ;
   num_modules++ ;
   }


int check_using(void)
   {
   if (filename[0] == '\0')
      {
      fprintf (stderr,"No working library has been defined\n");
      return FALSE;
      }
   return TRUE ;
   }


list_directory(void)
   {
   if (!check_using())
      return ;
   printf ("\nContents of library: %s\n\n",filename) ;
   num_modules = 0 ;
   traverse_tree (tree,list_node) ;
   if (num_modules)
      printf ("\nTotal of %d modules\n",num_modules) ;
   else printf ("No modules found\n") ;
   }


insert_module(void)
   {
   char name[MAX_NAME],filename[100] ;
   tree_ptr ptr ;
   record data ;
   char *filename_ptr ;

   if (!check_using())
      return ;
   strcpy (name,get_string (NULL,"Name of module to insert")) ;
   if (strlen(name) > MAX_NAME)
      {
      fprintf (stderr,"Name too long\n") ;
      return ;
      }
   if ((ptr=search_tree(tree,name)) != NULL)
      fprintf (stderr,"Module \"%s\" already exists in library\n",name) ;
   else
      {
      strcpy (filename,get_string(NULL,"Name of object file"));
      if (!valid_obj_file(filename))
         return ;
      strcpy (data.name,name) ;
      data.offset = INT_MAX ;         /* ensure end of list */
      ptr = insert_tree (tree,&data) ;
      ptr->new = modified = TRUE ;
      if ((ptr->filename = malloc (strlen(filename)+1)) == NULL)
         no_room() ;
      strcpy (ptr->filename,filename) ;
      modify_offsets (name,INSERT,tree) ;
      }
   }

valid_obj_file (char *name)
   {
   char *ch = compiler_header ;
   FILE *obj_fp ;

   if ((obj_fp=fopen(name,"r")) == NULL)
      {
      fprintf (stderr,"Object file not found\n") ;
      return FALSE ;
      }
   while (*ch == (unsigned short)fgetc(obj_fp))
      if (*ch++ == '\n')
         break ;
   fclose (obj_fp) ;
   if (*--ch != '\n')
      {
      fprintf (stderr,"Invalid object file\n") ;
      return FALSE ;
      }
   return TRUE ;
   }

add_deleted_amount (tree_ptr cursor)
  {
  if (cursor->new_offset > deleted_offset)
     add_amount (cursor) ;
  }

no_module (char *name)
   {
   fprintf (stderr,"Module \"%s\" does not exist in library\n",name) ;
   }

delete_module(void)
   {
   char name[MAX_NAME] ;
   tree_ptr ptr ;      
   record data ;

   if (!check_using())
      return ;
   strcpy (name,get_string(NULL,"Name of module to delete")) ;
   if ((ptr=search_tree(tree,name)) == NULL)
     no_module (name) ;
   else
      {
      ptr->deleted = modified = TRUE ;
      modify_offsets (name,DELETE,tree) ;
      amount_to_add = -ptr->length ;
      deleted_offset = ptr->new_offset ;
      traverse_tree (tree,add_deleted_amount) ;
      }
   }

modify_offsets(char *name, int op,tree_ptr cursor)
   {
   amount_to_add = strlen (name) + SYMBOL_ADMIN_SIZE ;
   if (op==DELETE)
      amount_to_add = -amount_to_add ;
   traverse_tree (cursor,add_amount) ;
   }

extract_module(void)
   {
   char name[MAX_NAME], filename[100] ;
   FILE *file_fp ;
   int loop ;
   tree_ptr ptr ;
   
   if (!check_using())
      return ;
   strcpy (name,get_string(NULL,"Name of module to extract")) ;
   if ((ptr=search_tree(tree,name)) == NULL)
      no_module (name) ;
   else
      {
      if (ptr->new || ptr->deleted)
         {
         fprintf (stderr,"Cannot extract module\n") ;
         return ;
         }
      printf ("Name of file:") ;
      scanf ("%s",filename) ;
      file_fp = fopen (filename,"w") ;
      put_header (compiler_header,file_fp) ;
      fputc (0,file_fp) ;
      put_header (name,file_fp) ;
      bput (file_fp,'\r') ;
      printf ("Extracting...\n") ;
      fseek (fp,ptr->old_offset,SEEK_SET) ;
      for (loop = 1; loop < ptr->length ; loop++)
         bput (file_fp,bget(fp)) ;
      fclose(file_fp) ;
      }
   }
