/* LINTLIBRARY */


/*
 * saveimage.c --
 *
 * Function to save an executable copy of the current process's
 * image in a file.
 *
 */

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

#ifdef BSD
#include <sys/types.h>
#include <sys/stat.h>
#include <a.out.h>
#include <errno.h>

extern int errno;

#define	BUFSIZE		8192

extern long lseek(  );			/* For lint */
extern int getpagesize(  );
extern char *sbrk(  );

static int copy_file(  );
static int pad_file(  );


int
util_save_image( orig_file_name, save_file_name )
 char *orig_file_name;
 char *save_file_name;
{
	int origFd = -1, saveFd = -1;
	char *start_data, *end_data, *start_text, *end_round;
	struct exec old_hdr, new_hdr;
	struct stat old_stat;
	int n, page_size, length_text, length_data;

	if ( ( origFd = open( orig_file_name, 0 ) ) < 0 )
	{
		perror( orig_file_name );
		( void ) fprintf( stderr, "Cannot open original a.out file\n" );
		goto bad;
	}

	if ( fstat( origFd, &old_stat ) < 0 )
	{
		perror( orig_file_name );
		( void ) fprintf( stderr, "Cannot stat original a.out file\n" );
		goto bad;
	}

	/*
	 * Read the a.out header from the original file.
	 */
	if ( read( origFd, ( char * ) &old_hdr, sizeof( old_hdr ) ) !=
		 sizeof( old_hdr ) )
	{
		perror( orig_file_name );
		( void ) fprintf( stderr, "Cannot read original a.out header\n" );
		goto bad;
	}
	if ( N_BADMAG( old_hdr ) )
	{
		( void ) fprintf( stderr, "File %s has a bad magic number (%o)\n",
						  orig_file_name, old_hdr.a_magic );
		goto bad;
	}
	if ( old_hdr.a_magic != ZMAGIC )
	{
		( void ) fprintf( stderr, "File %s is not demand-paged\n",
						  orig_file_name );
		goto bad;
	}

	/*
	 * Open the output file.
	 */
	if ( access( save_file_name, /* F_OK */ 0 ) == 0 )
	{
		( void ) unlink( save_file_name );
	}
	if ( ( saveFd = creat( save_file_name, 0777 ) ) < 0 )
	{
		if ( errno == ETXTBSY )
		{
			( void ) unlink( save_file_name );
			saveFd = creat( save_file_name, 0777 );
		}
		if ( saveFd < 0 )
		{
			perror( save_file_name );
			( void ) fprintf( stderr, "Cannot create save file.\n" );
			goto bad;
		}
	}

	/*
	 * Find out how far the data segment extends.
	 */
	new_hdr = old_hdr;
	end_data = sbrk( 0 );
	page_size = getpagesize(  );
	n = ( ( ( ( int ) end_data ) + page_size - 1 ) / page_size ) * page_size;
	end_round = ( char * ) n;
	if ( end_round > end_data )
	{
		end_data = sbrk( end_round - end_data );
	}

#ifdef vax
	start_text = 0;
	length_text = new_hdr.a_text;
	start_data = ( char * ) old_hdr.a_text;
	length_data = end_data - start_data;
#endif	/* vax */
#ifdef	sun
	start_text = ( char * ) N_TXTADDR( old_hdr ) + sizeof( old_hdr );
	length_text = old_hdr.a_text - sizeof( old_hdr );
	start_data = ( char * ) N_DATADDR( old_hdr );
	length_data = end_data - start_data;
#endif	/* sun */
	new_hdr.a_data = end_data - start_data;
	new_hdr.a_bss = 0;

	/*
	 * First, the header plus enough pad to extend up to N_TXTOFF.
	 */
	if ( write( saveFd, ( char * ) &new_hdr, ( int ) sizeof( new_hdr ) ) !=
		 sizeof( new_hdr ) )
	{
		perror( "write" );
		( void ) fprintf( stderr, "Error while copying header.\n" );
		goto bad;
	}
	if ( !pad_file( saveFd, N_TXTOFF( old_hdr ) - sizeof( new_hdr ) ) )
	{
		( void ) fprintf( stderr, "Error while padding.\n" );
		goto bad;
	}


	/*
	 *  Copy our text segment
	 */
	if ( write( saveFd, start_text, length_text ) != length_text )
	{
		perror( "write" );
		( void ) fprintf( stderr, "Error while copying text segment.\n" );
		goto bad;
	}


	/*
	 *  Copy our data segment
	 */
	if ( write( saveFd, start_data, length_data ) != length_data )
	{
		perror( "write" );
		( void ) fprintf( stderr, "Error while copying data segment.\n" );
		goto bad;
	}

	/*
	 * Copy the symbol table and everything else.
	 * This takes us to the end of the original file.
	 */
	( void ) lseek( origFd, ( long ) N_SYMOFF( old_hdr ), 0 );
	if ( !copy_file
		 ( origFd, saveFd, old_stat.st_size - N_SYMOFF( old_hdr ) ) )
	{
		( void ) fprintf( stderr, "Error while copying symbol table.\n" );
		goto bad;
	}
	( void ) close( origFd );
	( void ) close( saveFd );
	return 1;

  bad:
	if ( origFd >= 0 )
		( void ) close( origFd );
	if ( saveFd >= 0 )
		( void ) close( saveFd );
	return 0;
}


static int
copy_file( inFd, outFd, nbytes )
 int inFd, outFd;
 unsigned long nbytes;
{
	char buf[BUFSIZE];
	int nread, ntoread;

	while ( nbytes > 0 )
	{
		ntoread = nbytes;
		if ( ntoread > sizeof buf )
			ntoread = sizeof buf;
		if ( ( nread = read( inFd, buf, ntoread ) ) != ntoread )
		{
			perror( "read" );
			return ( 0 );
		}
		if ( write( outFd, buf, nread ) != nread )
		{
			perror( "write" );
			return ( 0 );
		}
		nbytes -= nread;
	}

	return ( 1 );
}


static int
pad_file( outFd, nbytes )
 int outFd;
 int nbytes;
{
	char buf[BUFSIZE];
	int nzero;

	nzero = ( nbytes > sizeof( buf ) ) ? sizeof( buf ) : nbytes;
	bzero( buf, nzero );
	while ( nbytes > 0 )
	{
		nzero = ( nbytes > sizeof( buf ) ) ? sizeof( buf ) : nbytes;
		if ( write( outFd, buf, nzero ) != nzero )
		{
			perror( "write" );
			return ( 0 );
		}
		nbytes -= nzero;
	}

	return ( 1 );
}
#else

/* ARGSUSED */
int
util_save_image( orig_file_name, save_file_name )
 char *orig_file_name;
 char *save_file_name;
{
	( void ) fprintf( stderr,
					  "util_save_image: not implemented on your operating system\n" );
	return 0;
}

#endif
