/*
 * decode.c
 *
 * Find all the symbols in an ALF or AOF file.
 *
 *  1994-1998 Straylight
 */

/*----- Licensing note ----------------------------------------------------*
 *
 * This file is part of Straylight's Dynamic Linking System (SDLS)
 *
 * SDLS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * SDLS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SDLS.  If not, write to the Free Software Foundation,
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "decode.h"
#include "aof/chunk.h"
#include "aof/aof.h"
#include "aof/alf.h"
#include "error.h"

static int decode__findChunk(chunk_header *c,char *name)
{
  int i;
  for (i=0;i<c->hdr.maxChunks;i++)
  {
    if (c->table[i].offset && !memcmp(c->table[i].chunkName,name,8))
      return (i);
  }
  return (-1);
}

static int decode__listALF(chunk_header *c,
                           hashtable h,
                           FILE *fp,
                           char *name,
                           int entry)
{
  int size=c->table[entry].size;	/* Help the compiler a bit	*/
  char *data=malloc(size);		/* Allocate memory for chunk	*/

  name=name;

  {
    if (!data)
      error(NOMEM);
  }

  {
    /* --- Note to the squeamish --- *
     *
     * I've done naughty things with fpos_t here to avoid reading the file
     * sequentially.  This file isn't doing anything particularly portable
     * anyway, so this is no great hardship.
     */

    fpos_t off;				/* To set file position		*/
    off.__lo=c->table[entry].offset;	/* Widge the position indicator	*/
    fsetpos(fp,&off);			/* Set the file offset nicely	*/
    fread(data,1,size,fp);		/* Read external names table	*/
  }

  {
    int i=0;				/* Counter through data		*/
    alf_symTable *tbl;			/* Pointer to actual structures	*/
    while (i<size)			/* Continue until no more	*/
    {
      tbl=(alf_symTable *)(data+i);	/* Set up the pointer		*/
      if (!hash_add(h,tbl->data))	/* Try to add the symbol name	*/
        error(NOMEM);
      i+=tbl->entryLength;		/* Bump the index along a bit	*/
    }
  }

  {
    free(data);
  }

  return (0);
}

static int decode__listAOF(chunk_header *c,
                           hashtable h,
                           FILE *fp,
                           char *name,
                           int sym,
                           int str)

{
  int sizeSym=c->table[sym].size;	/* Help the compiler a bit	*/
  int sizeStr=c->table[str].size;	/* Help the compiler a bit	*/
  aof_symbol *symbol=malloc(sizeSym);	/* Memory for symbol table	*/
  char *string=malloc(sizeStr);		/* Memory for string table	*/
  int symbols;				/* Number of symbol entries	*/
  int headoff=0;			/* Offset to AOF header chunk	*/
  int err=0;				/* No errors yet		*/

  /* --- Check we got the memory --- */

  {
    if (!symbol || !string)
      error(NOMEM);
  }

  if (!err)
  {
    if (headoff=decode__findChunk(c,"OBJ_HEAD"),headoff==-1)
    {
      error(BADFILE,name);
      err=1;
    }
  }

  if (!err)
  {
    /* --- Note to the squeamish --- *
     *
     * I've done naughty things with fpos_t here to avoid reading the file
     * sequentially.  This file isn't doing anything particularly portable
     * anyway, so this is no great hardship.
     */

    fpos_t off;				/* To set file position		*/

    off.__lo=c->table[headoff].offset+12l; /* Find number of symbols	*/
    fsetpos(fp,&off);			/* Set the file offset nicely	*/
    fread(&symbols,sizeof(symbols),1,fp); /* Read symbol count		*/
    off.__lo=c->table[sym].offset;	/* Widge the position indicator	*/
    fsetpos(fp,&off);			/* Set the file offset nicely	*/
    fread(symbol,1,sizeSym,fp);		/* Read symbol table		*/
    off.__lo=c->table[str].offset;	/* Widge the position indicator	*/
    fsetpos(fp,&off);			/* Set the file offset nicely	*/
    fread(string,1,sizeStr,fp);		/* Read string table		*/
  }

  if (!err)
  {
    int i;				/* Counter through data		*/
    for (i=0;i<symbols;i++)
    {
      if (symbol[i].defined && symbol[i].export)
      {
        if (!hash_add(h,string+symbol[i].name))
          error(NOMEM);
      }
    }
  }

  {
    free(string);
    free(symbol);
  }

  return (err);
}

int decode(hashtable h,char *filename)
{
  chunk_header *chf=0;			/* Ptr to block to read table	*/
  FILE *fp=fopen(filename,"rb");	/* Open for binary reading	*/
  int err=0;				/* No errors yet		*/

  {
    chunk_fixedHeader ch={0,0,0};	/* For reading in the header	*/

    if (!fp)				/* If open failed,...		*/
    {
      error(NOOPENIN,filename);		/* Report an error, and		*/
      err=1;				/*   return gracefully		*/
    }
    else
    {
      fread(&ch,sizeof(ch),1,fp);	/* Read the file's header	*/
      if (ch.id!=chunk_MAGIC)		/* Check it's a real chunk file	*/
      {
        error(BADFILE,filename);
        err=1;
      }
      else
      {
        chf=malloc(sizeof(chunk_fixedHeader)+
                   ch.maxChunks*sizeof(chunk_tableEntry));
        if (!chf)			/* If allocation failed		*/
          error(NOMEM);
        chf->hdr=ch;
        fread(chf->table,sizeof(chunk_tableEntry),ch.maxChunks,fp);
      }
    }
  }

  if (!err)
  {
    int index;				/* Index of symbol table chunk	*/
    int string;				/* Index of string table chunk	*/

    if (index=decode__findChunk(chf,"OFL_SYMT"),index!=-1)
      decode__listALF(chf,h,fp,filename,index);
    else if ( (index=decode__findChunk(chf,"OBJ_SYMT"),index!=-1) &&
              (string=decode__findChunk(chf,"OBJ_STRT"),string!=-1))
      decode__listAOF(chf,h,fp,filename,index,string);
    else
    {
      error(BADFILE,filename);
      err=1;
    }
  }

  {
    if (fp)
      fclose(fp);			/* Don't need this any more	*/
    free(chf);				/* Get rid of that too		*/
  }

  return (err);
}
