;/* how to compile this thingy:
gcc2 -s -O2 -mc68000 -DNDEBUG hunk2gcc.c -o hunk2gcc
quit
*/

/*
 * Convert amiga object files in the hunk format into a.out-object files.
 * ALINK style (ie. concatenated) libraries are supported as well, in that
 * case a collection of object files is generated, you'll have to run
 * `ar rs libfoo.a obj.*' on them to make an a.out-style library out of 
 * them.
 *
 * Currently untested are:
 *  o  base-relative relocs and references
 *     I'll first have to teach gnu-ld how to deal with them ;-)
 *  o  conversion of load files...
 *  o  common symbols, amiga objects normally don't use them, sigh
 *
 * Currently not implemented are:
 *  o  BLINK style indexed libraries (HUNK_LIB and HUNK_INDEX). If you're
 *     generating such libraries yourself, you can as well generate oldstyle
 *     libraries, and the libraries from Commodore-Amiga are in ALINK format
 *     (amiga.lib and friends).
 *     This index-format is so weird and complicated that I didn't bother
 *     long to decide not to support it. If someone volunteers (you got
 *     the source;-)) please send me enhancements!
 *
 * V1.0  91-10-08  M.Wild  first hack
 * V1.01 92-01-20  M.Wild  some pcrel code commented out, something is weird there..
 *
 * This is free software. This means that I don't care what you do with it
 * as long as you don't claim you wrote it. If you enhance it, please
 * send me your diffs! 
 * Oh, of course, you use this stuff entirely at your own risk, I'm not 
 * responsible for any damage this program might cause to you, your computer,
 * your data, your cat, your whateveryoucanthinkof, no warranty whatsoever is
 * granted.
 *
 * I can be reached on internet as: wild@nessie.cs.id.ethz.ch (Markus Wild)
 *
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
/* sun3-a.out file */
#include <a.out.h>
#include <stdio.h>
#include <stdlib.h>

/* This is really <dos/doshunks.h>, which is a 2.0 header file.
 * You can get those 2.0 headers from CATS.
 * Sorry, I'm not allowed to include them here. You may as well take the
 * AmigaDOS Manual 2nd ed. (you'll miss the a4-rel hunks) or 3rd ed. and write
 * the header yourself, it's not that long and difficult
 */
#include "doshunks.h"


#ifndef NDEBUG
#define DP(a) printf a
#else
#define DP(a)
#endif


extern int errno;

/* my favorite typedefs ;-)) */
typedef unsigned char uchar;
typedef unsigned long ulong;

ulong *file_buf = 0;

struct reloc {
  /* at which offset to relocate */
  int offset;
  /* based on which hunk base */
  int from_hunk, to_hunk;
  int size;
  int pcrel;
  int baserel;
  /* if != -1, the "to_hunk" field is not used */
  int sym_num;
};

/* NOTE: this symbol definition is compatible with struct nlist, and it will
 * be converted into a struct nlist in 'emit_aout_obj' */
struct symbol {
  int   name;		/* string as offset into string table */
  uchar type;
  uchar pad1;		/* really n_other */
  short hunk_num;	/* really n_desc  */
  ulong value;
};

struct table {
  void *base;
  int el_size;
  int i;
  int max_el;
};

void
emit_aout_file (int fd, void *text, void *data, struct exec *hdr,
		struct table *ch_tab, struct table *dh_tab, struct table *bh_tab,
		struct table *reloc_tab,  struct table *symbol_tab, int max_hunk);

#define TAB_START_SIZE 1024

/* advance the hunk-pointer to the next hunk. Assumes the hunk-pointer is
 * pointing at the length-field currently
 */
static void inline 
next_hunk(ulong **hp)
{
  /* skip over the length field and the there given length */
  *hp += 1 + **hp;
}

/****************************************************************************/

/* these two function maintain the string table. You may only free the last
 * string allocated. (This could be done with an obstack as well, but I really
 * don't need the whole functionality of an obstack, so I kept it simple ;-))
 * Only use the offset, since the table itself may be reallocated.
 */
char *str_table = 0;
int strtab_size, strtab_index;

static int inline
stralloc (int len)
{
  int res;

  /* always include the zero terminator */
  len++;

  if (! str_table)
    {
      strtab_size = TAB_START_SIZE;
      /* start the table at index 4, leaving space to later fill in the 
       * size of the table */
      strtab_index = 4;
      str_table = malloc (strtab_size);
    }

  while (strtab_index + len > strtab_size)
    {
      strtab_size <<= 1;
      str_table = realloc (str_table, strtab_size);
    }
  
  res = strtab_index;
  strtab_index += len;
  return res;
}

static void inline
strfree (int str)
{
  strtab_index = str;
}

/****************************************************************************/

static void inline
add_table (struct table *tab, void *el)
{
  if (tab->i == tab->max_el)
    {
      if (! tab->base)
        {
          tab->max_el = TAB_START_SIZE;
          tab->base = malloc (tab->max_el * tab->el_size);
        }
      else
	{
	  tab->max_el <<= 1;
	  tab->base = realloc (tab->base, tab->max_el * tab->el_size);
	}
      if (! tab->base)
        {
	  fprintf (stderr, "Out of memory adding to table.\n");
	  /* exit does close all outstanding files ;-) */
	  exit (20);
	}
    }
  bcopy (el, (uchar *)tab->base + (tab->i++ * tab->el_size), tab->el_size);
}

static void inline
add_reloc (struct table *tab, int from, int to, int offset,
           int size, int pcrel, int baserel, int sym_num)
{
  struct reloc r;

DP(("reloc: from=%d, to=%d, off=%d, size=%d, pc=%d, ba=%d, syn=%d\n",
    from, to, offset, size, pcrel, baserel, sym_num));

  r.from_hunk = from;
  r.to_hunk   = to;
  r.offset    = offset;
  r.size      = size;
  r.pcrel     = pcrel;
  r.baserel   = baserel;
  r.sym_num   = sym_num;
  add_table (tab, &r);
}

static void inline
add_symbol (struct table *tab, int num, ulong type, int value, char *name)
{
  struct symbol s;
  
  s.hunk_num = num;
  s.type     = type >> 24;
  s.value    = value;
  s.name     = stralloc ((type & 0xffffff)<<2);
  bcopy (name, str_table+s.name, (type & 0xffffff)<<2);
  (str_table+s.name)[(type & 0xffffff)<<2] = 0;
  
  /* some (really stupid..) compilers mention symbols twice, once as 
   * a definition, and once as an EXT_SYMB. So we really have to search 
   * the symbol_table before adding an EXT_SYMB and check if a symbol of this name
   * isn't already defined for this value. If this hack should slow down
   * the conversion dramatically, I'll have to resort to hashing, I don't
   * like that idea... */
  if (s.type == EXT_SYMB)
    {
      int i;
      
      for (i = 0; i < tab->i; i++)
	/* we have CSE in the compiler, right? ;-)) */
	if (((struct symbol *)tab->base)[i].value    == s.value    &&
	    ((struct symbol *)tab->base)[i].hunk_num == s.hunk_num &&
	    ! strcmp (str_table+((struct symbol *)tab->base)[i].name,
		      str_table+s.name))
	  {
	    strfree (s.name);
	    return;
	  }
    }
  
  add_table (tab, &s);
}

/****************************************************************************/

void
digest_objfile (ulong **file_pptr, ulong *max_fp)
{
  /* this makes it less clumsy.. */
  ulong *file_ptr = *file_pptr;
  static int obj_file_num = 0;
  int units = 0;
  /* to be filled in by the hunk_unit name parameter */
  int fd = -1;
  
  /* buffer-pointers, where text, data & bss sections are stored.
   * Currently only one hunk with size!=0 of type text/data/bss each is
   * supported */
  uchar *text, *data;
  struct exec hdr;
  
  /* hunk numbers, needed to associate reloc blocks with segments */ 
  int hunk_num;
  static struct table code_hunks = { 0, 4, 0, 0 };
  static struct table data_hunks = { 0, 4, 0, 0 };
  static struct table  bss_hunks = { 0, 4, 0, 0 };

  /* static so that no initialization code is required */
  static struct table reloc_tab  = { 0, sizeof (struct reloc), 0, 0 };
  static struct table symbol_tab = { 0, sizeof (struct symbol), 0, 0 };

  /* (re) init tables */
  strtab_index = 4;
  code_hunks.i =
    data_hunks.i =
      bss_hunks.i =
        reloc_tab.i =
          symbol_tab.i = 0;
  
  
  while (units < 2)
    {
      switch (*file_ptr++)
        {
        case HUNK_UNIT:
DP(("HUNK_UNIT: units = %d\n", units));
          if (units++) break;
#if 0
	  if (! file_ptr[0])
#endif
	    {
	      /* need [], not *, so that gcc allocates a fresh copy for
	       * mkstemp() to modify! */
	      char tmp_nam[] = "obj.YYYY.XXXXXX";
	      /* this trick makes mkstemp() lots faster ;-) */
	      sprintf (tmp_nam, "obj.%04d.XXXXXX", obj_file_num++);
	      if ((fd = mkstemp (tmp_nam)) < 0)
  	        fprintf (stderr, "Can't create object file: %s. Ignoring it.\n",
  	                 tmp_nam, strerror (errno));
	    }
#if 0
/* this idea was too good.. there are so many stupid (and duplicate!) names
 * of program units, that this generated ridiculous results... */

	  else
	    {
	      char *file_name;
	      file_name = alloca (file_ptr[0] + 1);
	      strncpy (file_name, (char *)&file_ptr[1], file_ptr[0]);
	      if ((fd = open (file_name, O_RDWR|O_CREAT|O_EXCL, 0666)) < 0)
  	        fprintf (stderr, "Can't create %s: %s. Ignoring it.\n",
  	                 file_name, strerror (errno));
	    }
#endif

	  /* init data for new object file */
	  text = data = 0;
	  bzero (&hdr, sizeof (hdr));
	  /* if someone want's to use'em on a sun, why shouldn't we make
	   * the files sun-conformant? */
	  hdr.a_toolversion = TV_SUN2_SUN3;
	  hdr.a_machtype = M_68010;	/* not restricted to 68020 only */
	  hdr.a_magic = OMAGIC;		/* relocatable format */
	  hunk_num = 0;
	  next_hunk (& file_ptr);
	  break;

        case HUNK_NAME:
        case HUNK_DEBUG:
DP(("HUNK_NAME/DEBUG\n"));
	  /* this hunk is silently ignored ;-) */
	  next_hunk (& file_ptr);
	  break;
	
        case HUNK_OVERLAY:
          fprintf (stderr, "warning: overlay hunk ignored!\n");
          next_hunk (& file_ptr);
          break;
        
        case HUNK_BREAK:
          fprintf (stderr, "warning: break hunk ignored!\n");
	  break;
	
        case HUNK_HEADER:
          fprintf (stderr, "warning: load file header encountered.\n");
          fprintf (stderr, "         are you sure this is an object file?\n");
          /* nevertheless, we go on. perhaps some day I need this feature to
           * be able to convert a loadfile into an object file?! */
         
          /* skip (never used) resident library names */
          while (file_ptr[0]) next_hunk (& file_ptr);
          /* skip null-word, table_size, F & L, and size-table */
          file_ptr += 4 + (file_ptr[1] - file_ptr[2] + 1);
          break;
        
        case HUNK_CODE:
DP(("HUNK_CODE, size = $%x\n", file_ptr[0] << 2));
	  if (file_ptr[0])
	    {
	      /* only accept one code hunk with size != 0 */
	      if (hdr.a_text)
	        fprintf (stderr, "only one code hunk with size!=0 supported.\n");
	      else
	        {
	      	  text = (uchar *)&file_ptr[1];
	      	  hdr.a_text = file_ptr[0] << 2;
	        }
	    }
	  next_hunk (& file_ptr);
	  add_table (& code_hunks, &hunk_num);
	  hunk_num++;
	  break;

        case HUNK_DATA:
DP(("HUNK_DATA, size = $%x\n", file_ptr[0] << 2));
	  if (file_ptr[0])
	    {
	      /* only accept one data hunk with size != 0 */
	      if (hdr.a_data)
	        fprintf (stderr, "only one data hunk with size!=0 supported.\n");
	      else
	        {
	      	  data = (uchar *)&file_ptr[1];
	      	  hdr.a_data = file_ptr[0] << 2;
	        }
	    }
	  next_hunk (& file_ptr);
	  add_table (& data_hunks, & hunk_num);
	  hunk_num++;
	  break;

        case HUNK_BSS:
DP(("HUNK_BSS, size = $%x\n", file_ptr[0] << 2));
	  if (file_ptr[0])
	    {
	      /* only accept one bss hunk with size != 0 */
	      if (hdr.a_bss)
	        fprintf (stderr, "only one bss hunk with size!=0 supported.\n");
	      else
	        hdr.a_bss = file_ptr[0] << 2;
	    }
	  file_ptr++;
	  add_table (& bss_hunks, & hunk_num);
	  hunk_num++;
	  break;

        case HUNK_RELOC8:
        case HUNK_RELOC16:
        case HUNK_RELOC32:
        /* do they work like this? don't know... */
        case HUNK_DREL8:
        case HUNK_DREL16:
        case HUNK_DREL32:
	  {
	    int size, brel;

	    brel = file_ptr[-1] >= HUNK_DREL32;
	    size = (brel ? HUNK_DREL8 : HUNK_RELOC8) - file_ptr[-1];
DP(("HUNK_RELOC/DREL ($%x), brel = %d, size = %d\n", file_ptr[-1], brel, size));
	    	    
	    while (file_ptr[0])
	      {
	        long to_reloc = file_ptr[1];
	        long n        = file_ptr[0];

	        while (n--)
	          /* amiga relocs are automatically pcrel, when size < 2
		   * according to the AmigaDOS-Manual 2nd ed. */
	          add_reloc (&reloc_tab, hunk_num-1, to_reloc, file_ptr[n+2],
	                     size, size != 2, brel, -1);
	    
	        file_ptr += file_ptr[0] + 2;
	      }
	  }
          file_ptr++;
          break;
        
        case HUNK_SYMBOL:
        case HUNK_EXT:
DP(("HUNK_SYMBOL/EXT\n"));
	  while (file_ptr[0])
	    {
	      int n, size, brel;

DP(("  EXT_: %d, %-*.*s\n", file_ptr[0] >> 24, 
    4*(file_ptr[0] & 0xffffff), 4*(file_ptr[0] & 0xffffff), &file_ptr[1]));

	      switch (file_ptr[0] >> 24)
	        {
	          case EXT_SYMB:
	          case EXT_DEF:
	          case EXT_ABS:
	          case EXT_RES:
	            add_symbol (&symbol_tab, hunk_num-1, file_ptr[0], 
			        file_ptr[1+(file_ptr[0] & 0xffffff)],
			        (char *)&file_ptr[1]);
		    file_ptr += 2+(file_ptr[0] & 0xffffff);
		    break;
		  
		  case EXT_COMMON:
		    /* first define the common symbol, then add the relocs */
		    add_symbol (&symbol_tab, hunk_num-1, file_ptr[0], 
		 	        file_ptr[1+(file_ptr[0] & 0xffffff)],
			        (char *)&file_ptr[1]);
		    file_ptr += 2+(file_ptr[0] & 0xffffff);

		    /* now the references, translated into relocs */
		    for (n = file_ptr[0]; n--; )
		      add_reloc (&reloc_tab, hunk_num - 1, -1, file_ptr[n],
			         2, 0, 0, symbol_tab.i - 1);
		    next_hunk (&file_ptr);
		    break;
		  
		  case EXT_REF8:
		  case EXT_REF16:
		  case EXT_REF32:
		  case EXT_DEXT8:
		  case EXT_DEXT16:
		  case EXT_DEXT32:
		    size = file_ptr[0] >> 24;
		    brel = size >= EXT_DEXT32;
		    size = (size == EXT_REF32 || size == EXT_DEXT32) ? 2 :
		  	   ((size == EXT_REF16 || size == EXT_DEXT16) ? 1 : 0);
		    /* first define the symbol (as undefined ;-)), 
		     * then add the relocs */
		    add_symbol (&symbol_tab, hunk_num-1, file_ptr[0], 
			        0, (char *)&file_ptr[1]);
		    file_ptr += 1+(file_ptr[0] & 0xffffff);

		    /* now the references, translated into relocs */
		    for (n = file_ptr[0]; n; n--)
		      add_reloc (&reloc_tab, hunk_num - 1, -1, file_ptr[n],
			         size, size < 2, brel, symbol_tab.i - 1);
		    next_hunk (&file_ptr);
		    break;

		  default:
		    fprintf (stderr, 
			     "Unknown symbol type %d, don't know how to handle!\n",
			     file_ptr[0] >> 24);
		    /* can't continue, don't know how much to advance the file_ptr
		     * to reach the next valid hunk/block */
		    exit(20);
	        }
	    }
	  file_ptr++;
	  break;

        case HUNK_END:
DP(("HUNK_END\n"));
	  break;

	default:
	  fprintf (stderr, "Unknown hunk type $%x, unit offset = $%x.\n",
	  	   file_ptr[-1], ((file_ptr-1)-*file_pptr) * 2);
	  /* can't continue, don't know how much to advance the file_ptr
	   * to reach the next valid hunk/block */
	  exit(20);
	}
      
      if (file_ptr >= max_fp) break;
    }

  *file_pptr = file_ptr >= max_fp ? max_fp : file_ptr-1;

  if (fd != -1)
    emit_aout_file (fd, text, data, & hdr, 
    		    & code_hunks, & data_hunks, & bss_hunks,
    		    & reloc_tab, & symbol_tab, hunk_num);
}


void
emit_aout_file (int fd, void *text, void *data, struct exec *hdr,
		struct table *ch_tab, struct table *dh_tab, struct table *bh_tab,
		struct table *reloc_tab,  struct table *symbol_tab, int max_hunk)
{
  int *code_hunks = ch_tab->base;
  int *data_hunks = dh_tab->base;
  int *bss_hunks  = bh_tab->base;
  char htype[max_hunk];  
  int i;
  struct reloc *r;
  struct symbol *s;
  static struct table text_relocs = { 0, sizeof (struct reloc_info_68k), 0, 0 };
  static struct table data_relocs = { 0, sizeof (struct reloc_info_68k), 0, 0 };

  text_relocs.i =
    data_relocs.i = 0;

  /* convert hunk-numbers into N_TEXT,N_DATA,N_BSS types */
  for (i = 0; i < ch_tab->i; i++) htype[code_hunks[i]] = N_TEXT;
  for (i = 0; i < dh_tab->i; i++) htype[data_hunks[i]] = N_DATA;
  for (i = 0; i < bh_tab->i; i++) htype[bss_hunks[i]]  = N_BSS;

  /* first conversion run. Change all hunk-numbers into N_TEXT, N_DATA and
   * N_BSS rsp.
   * (If you wanted to support multi-hunk files (ie. files with more than
   * one code/data/bss hunk with size > 0) you'd want to do the necessary
   * conversions here.)
   */
  for (i = 0, r = (struct reloc *)reloc_tab->base; i < reloc_tab->i; i++, r++)
    {
      r->from_hunk = htype[r->from_hunk];
      if (r->to_hunk > -1)
        r->to_hunk = htype[r->to_hunk];
    }
  for (i = 0, s = (struct symbol *)symbol_tab->base; i < symbol_tab->i; i++, s++)
    s->hunk_num = htype[s->hunk_num];
    
  /* now convert the symbols into nlist's */
  for (i = 0, s = (struct symbol *)symbol_tab->base; i < symbol_tab->i; i++, s++)
    {
      int nl_type = 0;

      switch (s->type)
	{
	case EXT_DEF:
	  /* externally visible symbol */
	  nl_type = N_EXT;
	  /* fall into */
	
	case EXT_SYMB:
	  nl_type |= s->hunk_num;
	  /* adjust DATA and BSS values to the one-seg model */
	  if (s->hunk_num == N_DATA)
	    s->value += hdr->a_text;
	  if (s->hunk_num == N_BSS)
	    s->value += hdr->a_text + hdr->a_data;
	  break;
	  
	case EXT_ABS:
	  nl_type = N_ABS | N_EXT;
	  break;
	  
	case EXT_COMMON:
	  /* ok for common as well, because the value field is only setup
	   * for common-symbols */

	case EXT_REF32:
	case EXT_REF16:
	case EXT_REF8:
	case EXT_DEXT32:
	case EXT_DEXT16:
	case EXT_DEXT8:
	  nl_type = N_UNDF | N_EXT;
	  break;

	default:
	  fprintf (stderr, "What kind of symbol is THAT? (%d)\n", s->type);
	  break;
	}

      s->type = nl_type;
      s->pad1 = s->hunk_num = 0;  /* clear nl_other & nl_desc fields */
    }
    
  /* now convert the reloc table. Adjust (like above) data/bss values to the
   * one-segment model for local relocations */
  for (i =  0, r = (struct reloc *)reloc_tab->base; i < reloc_tab->i; i++, r++)
    {
      struct reloc_info_68k rel;
      uchar *core_addr;
      
      rel.r_address = r->offset;
      core_addr = (r->from_hunk == N_TEXT) ? text : data;

      if (r->to_hunk == N_DATA)
	if (r->size == 2)
          *(ulong *)(core_addr + rel.r_address) += hdr->a_text;
        else
          fprintf (stderr, "%s reloc into N_DATA, what should I do?\n",
		   r->size == 1 ? "Short" : "Byte");
      else if (r->to_hunk == N_BSS)
	if (r->size == 2)
          *(ulong *)(core_addr + rel.r_address) += hdr->a_text + hdr->a_data;
        else
          fprintf (stderr, "%s reloc into N_BSS, what should I do?\n",
		   r->size == 1 ? "Short" : "Byte");

#if 0
      /* don't know, what went wrong, but this conversion surely converts
       * _LVO calls wrong. I'm sure leaving this out will generate other bugs..
       * sigh... */

      /* hmm... amigadog-hunks seem to do this in a strange way...
       * Those relocs *are* treated as pcrel relocs by the linker (blink), 
       * but they're not setup as such... geez, this hunk format.. */
      if (r->pcrel)
        switch (r->size)
	  {
	  case 2:
	    *(long *)(core_addr + rel.r_address)  -= rel.r_address;
	    break;

	  case 1:
	    *(short *)(core_addr + rel.r_address) -= rel.r_address;
	    break;
	    
	  case 0:
	    *(char *)(core_addr + rel.r_address)  -= rel.r_address;
	    break;
	  }
#endif

      rel.r_symbolnum = r->sym_num > -1 ? r->sym_num : r->to_hunk;
      rel.r_pcrel     = r->pcrel;
      rel.r_length    = r->size;
      rel.r_extern    = r->sym_num > -1;
      rel.r_baserel   = r->baserel;
      rel.r_jmptable = rel.r_relative = 0;

DP(("r68: %s reloc\n", (r->from_hunk == N_TEXT) ? "text" : "data"));
      add_table ((r->from_hunk == N_TEXT) ? & text_relocs : & data_relocs,
   		 &rel);
    }

DP(("r68: #tr = %d, #dr = %d\n", text_relocs.i, data_relocs.i));

  /* oky, fill out the rest of the header and dump everything */
  hdr->a_syms = symbol_tab->i * sizeof (struct nlist);
  hdr->a_trsize = text_relocs.i * sizeof (struct reloc_info_68k);
  hdr->a_drsize = data_relocs.i * sizeof (struct reloc_info_68k);
  *(ulong *)str_table = strtab_index;
  write (fd, (char *)hdr, sizeof (*hdr));
  if (hdr->a_text) write (fd, text, hdr->a_text);
  if (hdr->a_data) write (fd, data, hdr->a_data);
  if (hdr->a_trsize) write (fd, text_relocs.base, hdr->a_trsize);
  if (hdr->a_drsize) write (fd, data_relocs.base, hdr->a_drsize);
  if (hdr->a_syms) write (fd, symbol_tab->base, hdr->a_syms);
  write (fd, str_table, strtab_index);
  close (fd);
}



int
main (int argc, char *argv[])
{
  struct stat stb;
  int ret_val = 0;
  ulong *obj_pointer;
  
  FILE *fp;

  /* loop over all arguments. Can be either
   *  o  object files
   *  o  libraries (ALINK style for now)
   */
  while (*++argv)
    {
      if (stat (*argv, &stb) == -1)
        {
          fprintf (stderr, "%s: %s\n", *argv, strerror (errno));
          continue;
        }
      
      file_buf = file_buf ? realloc (file_buf, stb.st_size) 
			  : malloc (stb.st_size);
      if (! file_buf)
        {
          fprintf (stderr, "%s: can't allocate %d byte of memory.\n", 
		   *argv, stb.st_size);
	  ret_val = 20;
	  break;
	}
	
      if (!(fp = fopen (*argv, "r")))
        {
          fprintf (stderr, "Can't open %s: %s.\n", *argv, strerror (errno));
	  continue;
        }
      
      /* read the file in at once */
      if (fread (file_buf, stb.st_size, 1, fp) != 1)
        {
	  fprintf (stderr, "Can't read %s: %s.\n", *argv, strerror (errno));
	  fclose (fp);
	  continue;
	}
	
      /* oky, now digest the file (possibly more than one object file) */
      for (obj_pointer = file_buf;
	   obj_pointer < (ulong *)((uchar *)file_buf + stb.st_size); )
	digest_objfile (&obj_pointer, (ulong *)((uchar *)file_buf + stb.st_size));

      fclose (fp);
    }

  return ret_val;
}
