#include	<stdio.h>
#include	"lm.h"

extern	long	in_file, out_file;
extern	char	*get_next_file(), *in_file_name;
extern	BOOL	same_output_file;

struct symbol	*sym_list_head, *sym_ptr;

extern long	mod_buf[MBUF_SIZE];
int		eolib, symbol_offset;

unsigned long	buf_count, old_buf_count, current_long, temp;
void		*AllocMem();

long		Open(), Write();


/* --------------------------------------------------------------------------
			STRIP_LIBRARY ROUTINES
---------------------------------------------------------------------------*/

strip_library()
	{
	/* 
	  Read through the library and discard the symbol and debug
	  hunks.
	*/	
	unsigned	module_size;
	unsigned	long end_o_hunk = HUNK_END;

	printf( ">> Stripping: <%s>\n",in_file_name );

	while( (module_size = read_lib_module() ) )
		{
		/*
		  Strip symbol/debug hunks if there. All we do is write
		  back the module up to the symbol/debug hunk and stick
		  a HUNK_END on the end.
		*/
		if ( symbol_offset )
			{
			Write( out_file, mod_buf, (long) (symbol_offset << 2) );
			Write( out_file, &end_o_hunk, 4L );
			}

		else
			{
			Write( out_file, mod_buf, (long) (module_size << 2) );
			}
		}
	}




/* --------------------------------------------------------------------------
			ADD_MODULE ROUTINES
---------------------------------------------------------------------------*/

add_modules( file_list )
char	*file_list;

	{
	char		*p;
	long		fp;
	unsigned long	longword;

	copy_lib( in_file, out_file );

	while( (p = get_next_file( file_list ) ) )
		{
		
		if ( !(fp = Open( p, MODE_OLDFILE ) ) )
			{
			printf( "ERROR: Object file: <%s> not found\n", p );
			}
		else
			{
			printf( ">> Adding: <%s>\n", p );
			copy_lib( fp, out_file );
			Close( fp );
			}
		}
	}	




add_symbol( sym_name ) 
char	*sym_name;
	{
	/* 
	  Add the symbol in sym_name to the list of symbols.
	*/

	struct symbol	*p;

	if(!(p=(struct symbol*)AllocMem((long)sizeof(struct symbol), 0L)))
		{
		my_exit( ENOMEM );
		}

	p->next = NULL;
	sym_ptr->next = p;
	sym_ptr = p;

	strcpy( sym_ptr->name, sym_name );
	}
	


get_and_put()
	{
	/* 
	  Fetch a longword from the input file, stick it in the module
	  buffer, and return it. Return NULL if no more to read.
	*/
	long	longword;

	/*
	  First, make sure we are not overwriting innocent ram!
	*/
	if ( buf_count > MBUF_SIZE )
		{
		my_exit( ENOBUF );
		}


	if ( eolib )		/* End of library? */
		{
		my_exit( EOLIB );
		}

	if ( !GetLongword( in_file, &longword ) ) 
		{
		eolib = TRUE;
		return( TRUE );
		}

	mod_buf[buf_count++] = longword;
	current_long         = longword;
	return( TRUE );
	}
	

	
	
read_lib_module()
	{
	/* 
	  Read in the next module from the library, if one exists, and 
	  stick all the XDEF symbols in the symbol list. Return either
	  the number of longwords in the module or 0 to indicate the 
	  end of the library.
	*/

	clear_sym_list();		/* clean out prev sym list */
	sym_ptr	      = sym_list_head;	/* Reset sym ptr */			
	symbol_offset = 0;		/* Clear offset of symbol hunk ptr */
	buf_count     = 0;		/* Nothing in the buffer... */
	eolib	      = 0;		/* Not at end-of-library yet */

	while( get_and_put() )
		{
		if ( eolib )
			return( 0 );

		switch( current_long )
			{
			case HUNK_UNIT 	 :
			case HUNK_NAME 	 : get_named_hunk();
					   break;

			case HUNK_CODE 	 :
			case HUNK_DATA 	 : get_code_data_debug_hunk();
					   break;

			case HUNK_DEBUG  : if ( !symbol_offset )
					   	symbol_offset = buf_count-1;
			    
				     	   get_code_data_debug_hunk();
					   break;

			case HUNK_BSS  	 : get_and_put();
					   break;

			case HUNK_RELOC32:
			case HUNK_RELOC16:
			case HUNK_RELOC8 : get_reloc_hunk();
					   break;

			case HUNK_SYMBOL : get_symbol_hunk();
					   break;

			case HUNK_EXT	 : get_public_symbols();
					   break;

			case HUNK_END	 : return( buf_count );
					   break;
			}
		}
	}



get_public_symbols()
	{
	/* 
	  Stick the XDEF symbols in the HUNK_EXT hunk in the symbol
	  list.
	*/

	unsigned long	size, ref_count, name_buf[10], i;
	unsigned char	type;

	get_and_put(); /* read first symbol data unit */
	
	while ( current_long ) 	/* repeat til 0 */
		{
		size = current_long & 0xffffff;	/* Strip type field */
		type = current_long >> 24;

		for ( i = 0; i < size; i++ )
			{
			get_and_put();
			name_buf[i] = current_long;
			}
		name_buf[size] = '\0';  /* null term the string */

		switch( type )
		{

		case ext_symb	: 
		case ext_def	:
		case ext_abs	:
				  /* Eat the Offset longword */
		case ext_res	: get_and_put();
				  add_symbol( name_buf );
			  	  break;

		/* 
		  The rest of this switch statement just eats the infor-
		  mation contained in the hunk because we don't need it.
		*/

		case ext_ref32  : goto skip;

		case ext_common	: /* The next field is the size field */
				  get_and_put();

skip:		case ext_ref16	: /* Eat the ref count and the ref
				     offset fields */

		case ext_ref8	: get_and_put();	
				  ref_count = current_long;

				  for ( i = 0; i < ref_count; i++ )
				  	get_and_put();
				  break;
		}	

		get_and_put();
		}
	}



get_named_hunk()

	{
	/*
	  Read in the named hunk (HUNK_NAME/HUNK_UNIT).
	*/
	register int i;

	get_and_put();
	temp = current_long;

	for( i = 0; i < temp; i++ )
		{
		get_and_put();
		}
	}


get_code_data_debug_hunk()
	{
	/* 
	  Read in the CODE, DATA or DEBUG hunk.
	*/
	register int i;

	get_and_put();
	temp = current_long;

	for ( i = 0; i < temp; i++ )
		get_and_put();		
	}


get_reloc_hunk()
	{
	/* 
	  Eat the info in the relocxx hunks.
	*/
	register int i;
	unsigned long relocs;

	get_and_put();

	while( current_long )
		{
		relocs = current_long;

		get_and_put();

		for( i = 0; i < relocs; i++ )
			get_and_put();

		get_and_put();
		}
	}


get_symbol_hunk()
	{
	/* 
	  Chew through the symbol hunk.
	*/

	unsigned long	ext_id;
	unsigned int	i;
	
	/*
	  Mark the place where the symbol hunk begins.
	*/
	symbol_offset = buf_count-1;	

	get_and_put();
	
	while ( current_long )
		{
		ext_id = current_long;

		for ( i = 0; i <= ext_id; i++ )
			get_and_put();

		get_and_put();
		}
	}		


clear_sym_list()
	{
	/* 
	  Destroy the symbol list if one exists, but leave the head node 
	  intact for future use and abuse.
	*/

	struct symbol	*p, *q = sym_list_head->next;

	while ( q )
		{
		p = q->next;
		
		FreeMem( q, (long) sizeof( struct symbol ) );
		q = p;
		}		

	sym_list_head->next = NULL;
	}


copy_lib( in_file, out_file )
long	in_file, out_file;
	{
	char	*p;
	long	count, Read();

	if ( !( p = AllocMem( READBUFSIZE, 0L ) ) )
		{
		my_exit( ENOMEM );
		}
		
		
	while( ( count = Read( in_file, p, READBUFSIZE ) ) )
		{
		Write( out_file, p, count );
		}

	FreeMem( p, READBUFSIZE );
	}
