/**************************************************************************
* Copyright  (c) 2001 by Acunia N.V. All rights reserved.                 *
*                                                                         *
* This software is copyrighted by and is the sole property of Acunia N.V. *
* and its licensors, if any. All rights, title, ownership, or other       *
* interests in the software remain the property of Acunia N.V. and its    *
* licensors, if any.                                                      *
*                                                                         *
* This software may only be used in accordance with the corresponding     *
* license agreement. Any unauthorized use, duplication, transmission,     *
*  distribution or disclosure of this software is expressly forbidden.    *
*                                                                         *
* This Copyright notice may not be removed or modified without prior      *
* written consent of Acunia N.V.                                          *
*                                                                         *
* Acunia N.V. reserves the right to modify this software without notice.  *
*                                                                         *
*   Acunia N.V.                                                           *
*   Vanden Tymplestraat 35      info@acunia.com                           *
*   3000 Leuven                 http://www.acunia.com                     *
*   Belgium - EUROPE                                                      *
**************************************************************************/

/*
** $Id: class.c,v 1.3 2002/07/23 12:48:46 buytaert Exp $
*/

#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include "class.h"
#include "resource.h"
#include "argument.h"

typedef unsigned char             t_u1;
typedef unsigned short            t_u2;
typedef unsigned int              t_u4;
typedef unsigned char **          t_pool;

/*
** The different tags of the constant pool structures.
*/

#define TAG_CLASS                 7
#define TAG_FIELD                 9
#define TAG_METHOD               10
#define TAG_INTERFACE            11
#define TAG_STRING                8
#define TAG_INT                   3
#define TAG_FLOAT                 4
#define TAG_LONG                  5
#define TAG_DOUBLE                6
#define TAG_NAT                  12
#define TAG_UTF8                  1

t_u2 stream_u2(unsigned char *s) {

  t_u2 value;
  unsigned char * valuep = (unsigned char *)&value;
  
#ifdef LITTLE_ENDIAN
  valuep[0] = s[1];
  valuep[1] = s[0];
#else
  valuep[0] = s[0];
  valuep[1] = s[1];
#endif

  return value;
  
}

t_u4 stream_u4(unsigned char *s) {

  t_u4 value;
  unsigned char * valuep = (unsigned char *)&value;
  
#ifdef LITTLE_ENDIAN
  valuep[0] = s[3];
  valuep[1] = s[2];
  valuep[2] = s[1];
  valuep[3] = s[0];
#else
  valuep[0] = s[0];
  valuep[1] = s[1];
  valuep[2] = s[2];
  valuep[3] = s[3];
#endif

  return value;

}

#define read_u2(s) stream_u2(s); s += sizeof(unsigned short)
#define read_u4(s) stream_u4(s); s += sizeof(unsigned int)
#define read_u1(s) (*(s)); s += sizeof(unsigned char)

char * read_utf8(size_t length, unsigned char * bytes) {

  char * buffer = jj_calloc(length + 1, sizeof(unsigned int));
  
  if (buffer) {
    memcpy(buffer, bytes, length);
  }

  return buffer;
    
}

unsigned char * pool_read(t_pool * poolref, j_class class, unsigned char * s, t_u2 num) {

  t_u1 tag;
  t_u2 length;
  int i;
  t_pool pool;

  pool = jj_calloc(num, sizeof(unsigned char *));

  for (i = 1; i < num; i++) {
    pool[i] = s;
    tag = read_u1(s);
    switch (tag) {
      case TAG_CLASS: {
        s += sizeof(t_u2);
        break;
      }
      
      case TAG_FIELD: {
        s += sizeof(t_u2);
        s += sizeof(t_u2);
        break;
      }
      
      case TAG_METHOD: {
        s += sizeof(t_u2);
        s += sizeof(t_u2);
        break;
      }
      
      case TAG_INTERFACE: {
        s += sizeof(t_u2);
        s += sizeof(t_u2);
        break;
      }
      
      case TAG_STRING: {
        s += sizeof(t_u2);
        break;
      }
      
      case TAG_INT: {
        s += sizeof(t_u4);
        break;
      }
      
      case TAG_FLOAT: {
        s += sizeof(t_u4);
        break;
      }
      
      case TAG_LONG: {
        s += sizeof(t_u4);
        s += sizeof(t_u4);
        i += 1;                   // These take 2 pool entries !!
        break;
      }
      
      case TAG_DOUBLE: {
        s += sizeof(t_u4);
        s += sizeof(t_u4);
        i += 1;                   // These take 2 pool entries !!
        break;
      }
      
      case TAG_NAT: {
        s += sizeof(t_u2);
        s += sizeof(t_u2);
        break;
      }

      case TAG_UTF8: {
        length = read_u2(s);
        s += length;
        break;
      }
      
      default: {
        logmsg(10, "class constant pool tag '%d' at index %d not valid. Class skipped.\n", tag, i);
        jj_free(pool);
        *poolref = NULL;
        return s;
      }

    }
  }

  *poolref = pool;
  
  return s;
  
}

j_class class_read(const char * file, size_t size) {

  size_t buffer_size;
  unsigned char * buffer;
  unsigned char * stream;
  unsigned char * cursor;
  struct stat fs;
  int fd;
  int status;
  j_class class;
  t_u4 u4;
  t_u2 u2;
  t_u1 tag;
  int i;
  int j = 0;
  int k;
  int index;
  int name_index;
  char * string;
  int length;
  t_pool pool;
  j_res new;
  j_res found;
  
  status = stat(file, &fs);
  if (status == -1) {
    return NULL;
  }
  
  buffer_size = size;
  buffer = jj_calloc(buffer_size, sizeof(unsigned char));
  if (buffer == NULL) {
    return NULL;
  }
  
  fd = open(file, O_RDONLY);
  if (fd == -1) {
    logmsg(11, "could not open '%s' (%s)\n", file, strerror(errno));
  }
  
  read(fd, buffer, buffer_size);
  close(fd);

  logmsg(5, "Parsing class file '%s' %d bytes.\n", file, buffer_size);
    
  stream = buffer;
  u4 = read_u4(stream);                            // read the classfile signature
  if (u4 != 0xcafebabe) {
    logmsg(10, "not a class file '%s' sig = 0x%08x\n", file, u4);
    jj_free(buffer);
    return NULL;
  }

  stream += sizeof(t_u2);                          // minor version
  stream += sizeof(t_u2);                          // major version

  u2 = read_u2(stream);                            // number of constant pool entries

  /*
  ** Allocate a class structure that can hold as many inner class filenames as there
  ** are constant pool entries divided by 2. This is guaranteed large enough to hold
  ** all inner classes names. We later realloc this memory.
  */
  
  class = jj_calloc(1, sizeof(j_Class) + ((u2 / 2) * sizeof(j_res)));
  class->size = buffer_size;
  stream = pool_read(&pool, class, stream, u2);
  if (pool == NULL) {
    logmsg(10, "Could not read constant pool of '%s'. Class skipped.\n", file);
    jj_free(class);
    jj_free(buffer);
    return NULL;
  }
  
  stream += sizeof(t_u2);                          // access flags
  name_index = read_u2(stream);                    // name index in pool
  stream += sizeof(t_u2);                          // super class index
  u2 = read_u2(stream);                            // read number of interfaces
  stream += (u2 * sizeof(t_u2));                   // skip over the interfaces table

  u2 = read_u2(stream);                            // number of fields
  for (i = 0; i < u2; i++) {
    stream += sizeof(t_u2);                        // access flags
    stream += sizeof(t_u2);                        // name index
    stream += sizeof(t_u2);                        // descriptor index
    j = read_u2(stream);                           // attributes count
    while (j--) {
      stream += sizeof(t_u2);                      // attribute name index
      k = read_u4(stream);                         // attribute length
      stream += k;                                 // skip over field attribute info
    }
  }

  u2 = read_u2(stream);                            // number of methods
  for (i = 0; i < u2; i++) {
    stream += sizeof(t_u2);                        // access flags
    stream += sizeof(t_u2);                        // name index
    stream += sizeof(t_u2);                        // descriptor index
    j = read_u2(stream);                           // attributes count
    while (j--) {
      stream += sizeof(t_u2);                        // attribute name index
      k = read_u4(stream);                         // attribute length
      stream += k;                                 // skip over method attribute info
    }
  }

  k = read_u2(stream);                             // number of class attributes

  j = 0;
  for (i = 0; i < k; i++) {
    index = read_u2(stream);
    cursor = pool[index]; 
    tag = read_u1(cursor);                         // skip UTF8 tag
    length = read_u2(cursor);                      // read UTF8 length
    string = read_utf8(length, cursor);
    if (strcmp(string, "InnerClasses") == 0) {
      jj_free(string);
      u4 = read_u4(stream);                        // Attribute length
      u2 = read_u2(stream);                        // Number of inner classes
      class->num_inner = u2;
      for (j = 0; j < u2; j++) {
        index = read_u2(stream);
        cursor = pool[index];                      // Get Class info structure
        cursor += sizeof(t_u1);                    // Skip over the tag (could check it)
        index = read_u2(cursor);                   // Get name index
        cursor = pool[index];                      // Jump to UTF8 structure
        tag = read_u1(cursor);
        length = read_u2(cursor);
        string = read_utf8(length, cursor);

        /*
        ** See if the resource is allready in the hashtable. If yes, we take the existing
        ** entry and free the new one, otherwise, we add and use the new one.
        */

        logmsg(3, "inner class found '%s'.\n", string);
        new = res_class(string);
        found = res_exists(new);
        if (found) {
          class->inner[j] = found;
          res_free(new);
        }
        else {
          class->inner[j] = new;
          res_add(new);
        }
        class->inner[j]->outer = class;
        jj_free(string);

        index = read_u2(stream);
        index = read_u2(stream);
        index = read_u2(stream);
      }
    }
    else {
      jj_free(string);
      u4 = read_u4(stream);
      stream += u4;
    }
  }

//  class = jj_realloc(class, sizeof(j_Class) + (j * sizeof(j_res)));

  /*
  ** Convert the class name index into a real string.
  */
  
  stream = pool[name_index];
  stream += sizeof(t_u1);
  u2 = read_u2(stream);
  name_index = u2;
  cursor = pool[name_index];
  tag = read_u1(cursor);
  length = read_u2(cursor);
  string = read_utf8(length, cursor);
  class->name = string;

  logmsg(4, "Java class '%s' has %d inner class%s.\n", string, j, (j != 1) ? "es" : "");
  
  class->data = buffer;
   
  jj_free(pool);

  return class;
  
}

void free_class(j_class class) {

}
