/* gcv.c, Author: Marc Feeley (08/01/90)
 *               Modified for the Amiga by Ken Dickey
 *               with special thanks to Doug Williams for hunkification
 */

/*

This program converts Gambit .O files (object files) into .o files
that can be linked with 'glk' to form an executable program.

The .O file is first transformed into a C file and then compiled
with cc.

To convert the file 'file.O' it should be called like this

   gcv file

*/

#include <stdio.h>
/* include <sys/types.h>
   include <sys/stat.h>
   include <sys/wait.h>
*/

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

/* string utils */


char *alloc( n )
int n;
{ char *p = (char *)malloc( n );
  if (p == NULL)
  { fprintf( stderr, "Error: out of memory\n" ); exit(1); }
  return p;
}


int string_length( str )
char *str;
{ int len = 0;
  while (*(str++) != '\0') len++;
  return len;
}


char *string_extend( ptr, str )
char *ptr, *str;
{ while (*str != '\0') *(ptr++) = *(str++);
  *ptr = '\0';
  return ptr;
}


char *string_append( str1, str2 )
char *str1, *str2;
{ char *p1;
  p1 = alloc( string_length( str1 ) + string_length( str2 ) + 1 );
  string_extend( string_extend( p1, str1 ), str2 );
  return p1;
}


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


FILE *input, *output;

short *data;

char *argvect[10];


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


char *filename_tail( str )
char *str;
{ char *s = str, *p = str;
  while (*p != '\0') if (*(p++) == '/') s = p;
  return s;
}


void generate_filename_key( output, str )
FILE *output;
char *str;
{ char *s = filename_tail( str );
  while (*s != '\0')
  { char c = *(s++);
    if (((c>='a') && (c<='z')) || ((c>='0') && (c<='9')) || (c=='_'))
      fprintf( output, "%c", c );
    else
      fprintf( output, "X%02x", (c & 0xff) );
  }
}

long os_file_length( f )
FILE *f;
{ /* for the AMIGA/LATTICE -- kad */
  long  orig_pos, end_pos;

  orig_pos = lseek( f->_file, 0L, 1 ) ;  /* ftell() */
  end_pos  = lseek( f->_file, 0L, 2 ) ;  /* find the end */
  lseek( f->_file, orig_pos, 0 ) ;       /* reset */
/*@@debug
  fprintf( stderr, "os_file_length: %ld\n", end_pos);
@@*/
  return( end_pos ) ;
}


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

/* code & definitions for a BLink compatible object file (drw) */

#define H_DEF  (1<<24)
#define H_UNIT 0x3E7
#define H_DATA 0x3EA
#define H_EXT  0x3EF
#define H_END  0x3F2

#define btol(n)          (((n)+3)/sizeof(long))
#define write_byte(f,b)  {char w=(b); fwrite((char*)&w,sizeof(char),1,f);}
#define write_short(f,s) {short w=(s); fwrite((char*)&w,sizeof(short),1,f);}
#define write_long(f,l)  {long w=(l); fwrite((char*)&w,sizeof(long),1,f);}


void align_data (output, size)
FILE *output;
{ while (size%sizeof(long) != 0) {
    fputc (0, output);
    size++;
  }
}


void write_ext_def (fp, name, offset)
FILE *fp;
char *name;
{ long len = strlen (name);
  write_long (fp, H_DEF | btol(len));
  fprintf (fp, name);
  align_data (fp, len);
  write_long (fp, offset);
}


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


void main( argc, argv )
int argc;
char *argv[];
{ long file_size;
  int i, n ;
  char *o_file, *O_file ;

  if (argc != 2)
  { fprintf( stderr, "Usage: gcv <file>  -- converts file.O to file.o \n" ); exit(1); }

  o_file = string_append( argv[1], ".obj" );
  O_file = string_append( argv[1], ".O" );

  input = fopen( O_file, "r" );
  if (input == NULL)
  { fprintf( stderr, "Error: can't open input file %s\n", O_file ); exit(1); }

  output = fopen( o_file, "w" );
  if (output == NULL)
  { fprintf( stderr, "Error: can't open output file %s\n", o_file ); exit(1); }

  /* get info from input file */

  file_size = os_file_length( input ) ;

  if ((file_size <= 0) || (file_size & 1 != 0))
  { fprintf( stderr, "Error: alignment error\n" ); exit(1); }

  data = (short *)alloc( file_size );

  if (fread( (char *)data, 1, file_size, input ) != file_size)
  { fprintf( stderr, "Error: read error\n" ); exit(1); }

   /* write program unit header */
  
  write_long( output, H_UNIT );
  write_long( output, btol( n=strlen( O_file )));
  fprintf( output, O_file );
  align_data( output, n );

  /* output data hunk */

  write_long( output, H_DATA );
  write_long( output, 1 + btol( file_size ));

  /* write link_ofile_* data */


  i = 0;
  n = file_size/2;
  while (i < n)
    data[i++] &= 0xffff;
  fwrite( (char *)data, sizeof(short), file_size/2, output );

  /* write link_sizeof_ofile_* data */

  write_long( output, file_size ); 
  align_data( output, file_size + sizeof(long) );

  /* external definition hunk for link_* symbols */ 

  write_long( output, H_EXT );
  write_ext_def( output, string_append( "_link_ofile_", argv[1] ), 0);
  write_ext_def( output, string_append( "_link_sizeof_ofile_", argv[1] ),
		 file_size );
  write_long( output, 0 );

  write_long( output, H_END );
  fclose( output );

  fprintf( stderr, "Created object file: \"%s\"\n", o_file );

  exit(0);
}


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