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

/* ----------------------------------------------------------------------------

				LM.C

The amiga library manager.


ChangeStuff:

	- Make the module buffer variable sized! maybe a command line flag...

----------------------------------------------------------------------------- */

void		*AllocMem();
char		*strchr();

extern	void	list_library();
extern	void	add_modules();
extern	void	delete_modules();
extern	void	replace_modules();
extern	void	strip_library();
extern	void	extract_modules();
extern	void	do_interactive_stuff();


long	DeleteFile(), Rename(), IoErr();
long	in_file, out_file;

char	*get_next_file();			/* the file list parser */
FILE 	*list_file;

char	temp_file_name[] = "lm_temp";
char	new_file_name[44];
BOOL	same_output_file = FALSE;

char	Greeting[] = 
"Amiga library manager v1.0\n\
Copyright 1988 by SLADE software\n\n";

char	Usage[] = 
"<input library name> <command> [output library name]\n\
Where <command> is one of the following:\n\
	DELETE\t\t\t -d<module>[+module]...\n\
	ADD\t\t\t -a<object-file>[+object-file]...\n\
	EXTRACT\t\t\t -e<module>+[module]...\n\
	STRIP SYMBOL/DEBUG HUNKS -s\n\
	REPLACE\t\t\t -r<object-file>[+object-file]...\n\
	LIST\t\t\t -l[list-file]\n\
	INTERACTIVE MODE\t -i\n";


struct symbol	*sym_list_head;
struct fname	*mod_list_head;

char		*in_file_name, *out_file_name;

long		mod_buf[MBUF_SIZE];
long		*mod_buf_ptr;

int		name_count;		/* count of user-supplied names */
int		buffer_size;		/* buffer size in longwords	*/


main( argc, argv )
int	argc;
char	**argv;
	{
	char	**p = argv+1, *arg_string, choice;
	long	Open();

	printf( "%s", Greeting );   /* Say hello to joe user */

	if ( argc < 3 )  /* Too few arguments supplied */
		{
  		printf( "Usage: %s %s", *argv, Usage ); 
		my_exit( 100 );
		}

	/*
	  Parse the arguments.
	*/
	in_file_name = *p++;			/* get pointer to input lib */
	arg_string = *p;  			/* get pointer to arg str   */
	choice = tolower( *++arg_string );	/* get requested operation  */

	if ( argc > 3 )
		out_file_name = *++p; 		/* get output file spec     */


	/*
	  If we are adding modules to the library, it will be created if
	  it does not exist. For all other operations, non-existance is
	  an error.
	*/
	if ( !( in_file = Open( in_file_name, MODE_OLDFILE ) ) )
		{
		if ( choice == ADD )
			get_input_file( in_file_name );
		else
			my_exit( ENOLIB );
		}

	/* 
	  Initialize dynamic lists.
	*/
	init_lists();
		

	/*
	  First we look for commands that do not require an output file.
	*/
	++arg_string;

	if ( choice == LIST )
		{
		list_library( arg_string );
		my_exit( 0 );
		}


	if ( choice == EXTRACT )
		{
		extract_modules( arg_string );		
		my_exit( 0 );
		}


	open_output();		/* open output file if possible */

	switch( choice )
		{
		case	ADD	: add_modules( arg_string );
				  break;

		case	DELETE	: delete_modules( arg_string );
				  break;

		case	INTERACT: do_interactive_stuff();
				  break;

		case	REPLACE : replace_modules( arg_string );
				  break;

		case	STRIP	: strip_library();
				  break;

		default		: my_exit( EBADOPT, arg_string );
		}

	/*
	  De-alloc all of our memory, and close the files
	*/

	my_exit( 0 );
	}


get_input_file( in_file_name )
char	*in_file_name;
	{
	printf( "Library File: <%s> not found, Create? (y/n) ", in_file_name );

	if( tolower( getchar() ) == 'n' )
		{
		printf( "\nBye Bye!\n" );
		my_exit( 0 );
		}

	printf( "\n" );

	if ( !( in_file = Open( in_file_name, MODE_NEWFILE ) ) )		
		{
		my_exit( ENOINPUT, in_file_name );
		}
	}



open_output()
	{
	/* 
	  If there was no output file specified, or it is the same as
	  the input file, we will open a temp file until done, and then 
	  replace the original file with it.
	*/

	char 	*p, *q;

	if ( !out_file_name || !strcmp( in_file_name, out_file_name ) )
		{
		same_output_file = TRUE;

		/*
		  If the input file name includes a device name, we must
		  copy it to the output file name or else Rename will not
		  work.
		*/

		if ( (p = strchr( in_file_name, ':' ) ) )
			{
			for( q = new_file_name, p = in_file_name; *p != ':'; )
				*q++ = *p++;

			*q = ':';
			
			strcat( new_file_name, temp_file_name );
			out_file_name = new_file_name;
			}
		else
			out_file_name = temp_file_name;

		}
	else	same_output_file = FALSE;


	/* 
	  Attempt to open the output file.
	*/

	if ( !( out_file = Open( out_file_name, MODE_NEWFILE ) ) )
		{
		my_exit( ENOOUTPUT, out_file_name );
		}


	}		


char *get_next_file( file_list )
char	*file_list;
	{
	static char	*next_file = NULL;
	static BOOL	first_time = TRUE;
	char		*p;

	/* 
	  Return the next file in the '+' delimited file list or
	  NULL if no more exist.
	*/

	if ( first_time )
		{
		first_time = FALSE;
		p = file_list;
		}

	else	p = next_file;

	next_file = strchr( p, '+' );
	
	if ( *next_file )
		*next_file++ = '\0';
	
	return( p );
	}


init_lists()
	{
	/* 
	  Initialize the symbol and the name lists by allocating
	  the first nodes of each.
	*/
	
	if ( !( mod_list_head = (struct fname *)  
		AllocMem( (long) sizeof( struct fname ), 0L ) ) ||
	     !( sym_list_head = (struct symbol *) 
	        AllocMem( (long) sizeof( struct symbol ), 0L ) ) )
		{
		my_exit( ENOMEM );
		}
	
	mod_list_head->next = NULL;
	sym_list_head->next = NULL;
	}


create_name_list( list )
char	*list;

	{
	/* 
	  Build a linked list out of the user supplied file list.
	*/
 
	struct fname 	*p = mod_list_head, *q = mod_list_head->next;
	char		*file_name;

	/* 
	  Fill in head node.
	*/
	file_name = get_next_file( list );
	if ( !file_name )
		{
		my_exit( ENOARGS );
		}


	name_count = 1;	

	/* 
	  Copy the file name.
	*/
	copy_file_name( file_name, p );

	/* 
	  Now fill in the rest of the list.
	*/
	while( (file_name = get_next_file( list ) ) )
		{
		name_count++;

		if(!(q=(struct fname*)AllocMem((long)sizeof(struct fname),0L)))
			{
			my_exit( ENOMEM );
			}
		q->next = NULL;
		
		copy_file_name( file_name, q );

		/*
		  Hop down the list
		*/
		p->next = q;
		p = q;
		}
	}


copy_file_name( file_name, list_elem )
char		*file_name;
struct fname	*list_elem;

	{
	/*
	  Copy both the file name and the file name without the '.' to the
	  record.
	*/
	char	*p;

	strcpy( list_elem->name, file_name );
	strcpy( list_elem->filename, file_name );
	
	if ( (p = strchr( list_elem->name, '.' ) ) )
		*p = '\0';
	}


my_exit( rc, str )
int	rc;
char	*str;

	{
	/* 
	  Clean up the allocated memory, then quit.
	*/
	long	Close();

	if ( sym_list_head )
		{
		clear_sym_list();
		FreeMem( sym_list_head, (long) sizeof( struct symbol ) );
		}

	if ( mod_list_head )
		{
		clear_mod_list();
		FreeMem( mod_list_head, (long) sizeof( struct fname ) );
		}

	if ( in_file )
		Close( in_file );

	if ( out_file )
		Close( out_file );

	
	switch( rc )
	{
	case	ENOINPUT:  printf( "ERROR: Unable to open: <%s> \n", str  );
			   goto error_exit;

	case	ENOOUTPUT: printf( "ERROR: Unable to create: <%s>\n", str );
			   goto error_exit;

	case	ENOLIST:   printf( "ERROR: Unable to open list file: <%s>\n",str );
		           goto error_exit;

	case	ENOARGS:   printf( "ERROR: Missing arguments\n" );
			   goto error_exit;

 	case	ENOMEM:	   printf( "ERROR: Out of memory\n" );
			   goto error_exit;

 	case	EBADOPT:   printf( "ERROR: Bad option: <%ls>\n", str );
			   goto error_exit;
			   
	case	EABORT:	   printf( "Aborting program...\n" );
			   DeleteFile( out_file_name );
			   exit( rc );

	case	ENOBUF:    printf( "ERROR: Module larger than %dk\n", MBUF_SIZE/256);
			   goto error_exit;

	case    EOLIB:     printf( "ERROR: End of library detected\n" );
			   goto error_exit;

	case	ENOLIB:	   printf( "ERROR: Library <%s> does not exist\n", in_file_name );
			   
	error_exit:	   DeleteFile( out_file_name );
			   exit( rc );
	}

	if ( same_output_file )
		{
		if ( !DeleteFile( in_file_name ) )
			{
			printf( "ERROR: can't delete: <%s>\nDos error code: %ld\n", 
			in_file_name, IoErr() );
			}

		if ( !Rename( out_file_name, in_file_name ) ) 
			{
			printf( "ERROR: can't rename: <%s> as: <%s>\nDos error code: %ld\n", 
			out_file_name, in_file_name, IoErr() );
			}
		}

	exit( rc );
	}


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

	struct fname	*p, *q = mod_list_head->next;

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

	mod_list_head->next = NULL;
	}



GetLongword( fp, longword )
long	fp;
char	*longword;

	{
	static char	rbuf[READBUFSIZE];
	static long	bytes_read = 0;		/* bytes read in */
	static long	bc = 99;		/* buf counter */
	register	int i;
	long		Read();

	if ( bc >= bytes_read )	/* End of the buffer */
		{
		if ( !( bytes_read = Read( fp, &rbuf, READBUFSIZE ) ) )
			{
			return( FALSE );
			}
		bc = 0;
		}

	for( i = 0; i < 4; i++, *longword++ = rbuf[bc++] ) ;

	return( TRUE );
	}
