#include <stdlib.h>

#include "types.h"
#include "mpool.h"
#include "clstree.h"
#include "tktree.h"
#include "sha1.h"
#include "listen.h"
#include "rovm.h"

#include "thread.h"
#include "thread_mutex.h"
#include "thread_cond.h"

#include "mpm_worker_fdqueue.h"
#include "mpm_worker_pod.h"
#include "mpm_worker.h"

#include "connection.h"
#include "request.h"
#include "proc_rc.h"

#include "log.h"
#include "common.h"
#include "ticket.h"
#include "utils.h"

#include "class.h"

#define EFILE_PATH(NODE)                ((NODE)->filepath)
#define EFILE_BUFBASE(NODE)             ((NODE)->bufbase)
#define EFILE_BUFCUR(NODE)              ((NODE)->bufcur)
#define EFILE_BUFCUR_IDX(NODE, IDX)	((NODE)->bufcur[(IDX)])
#define EFILE_BUFLEN(NODE)              ((NODE)->buflen)

struct efile
{
  /** 현재 처리하고 있는 File 의 경로.  */
  u1 *filepath;

  u1 *bufbase;
  u1 *bufcur;
  u4 buflen;
};

/**
    EF 로부터 1 바이트를 C 에 저장하고, 위치를 이동한다.  
*/
static void
readu1 (ef, c)
     struct efile *ef;
     u1 *c;
{
  *c = EFILE_BUFCUR_IDX (ef, 0);
  EFILE_BUFCUR (ef) += 1;
}

/**
   EF 로부터 1 바이트를 C 에 저장하고, 위치를 이동한다.  
*/
static void 
readu2 (ef, c)
     struct efile *ef;
     u2 *c;
{
  *c = ((u2 *) &EFILE_BUFCUR_IDX (ef, 0))[0];
  EFILE_BUFCUR (ef) += 2;
}

/**
   EF 로부터 4 바이트를 C 에 저장하고, 위치를 이동한다.  
*/
static void 
readu4 (ef, c)
     struct efile *ef;
     u4 *c;
{
  *c = ((u4 *) (&EFILE_BUFCUR_IDX (ef, 0)))[0];
  EFILE_BUFCUR (ef) += 4;
}

/**
   struct environment_file 에서 LEN*SIZE 만큼 읽어서, DEST 에
   저장한다.
*/
static void
readm (ef, dest, len, size)
     struct efile *ef;
     void *dest;
     size_t len;
     size_t size;
{
  int tsize = len * size;

  memcpy (dest, EFILE_BUFCUR (ef), tsize);
  EFILE_BUFCUR (ef) += tsize;
}

/**
   ROVM 설정 파일의 ClassRoot 와 PATH 를 결합하여 전체 경로를 완성하여
   반환합니다.

   @param r     ROVM 구조체
   @param path  Class 파일의 상대 경로
 */
static char *
efile_getfullpath (r, path, postfix)
     struct rovm *r;
     char *path, *postfix;
{
  char *ret;

  if (!r || (r && !CONF_CLASSROOT (ROVM_CONF (r))))
    return NULL;

  ret = (char *) rc_calloc (1, 
                            strlen (CONF_CLASSROOT (ROVM_CONF (r))) 
                            + strlen (path) 
                            + strlen (DEFAULT_ENVLANG_EXTENSION)
                            + 1);
  strcat (ret, CONF_CLASSROOT (ROVM_CONF (r)));
  strcat (ret, path);
  strcat (ret, postfix);

  return ret;
}

/**
   파일이 존재하는지를 확인한 후, 이에 대한 반환값을 돌려줍니다.

   @param r     ROVM 구조체
   @param path  경로 (확장자가 빠져있습니다.)
   @param ext   확장자
   @return      만약 파일이 존재할 경우 1 을 그렇지 않을 경우 0 을 반환합니다.
 */
static int
rc_fileexist (r, path, ext)
     struct rovm *r;
     char *path, *ext;
{
  char *fp;
  struct stat fi; 

  fp = efile_getfullpath (r, path, ext);

  if (stat (fp, &fi))
    {
      rc_free (fp);
      return 0;
    }

  rc_free (fp);
  return 1;
}

/**
   앞으로 PATH 를 분석하게 될텐데, 그 때 사용하게 될 기본적인 정보 및 기타
   환경을 설정하고, 정보를 담은 struct efile 구조체를 반환한다.

   @param r     ROVM 구조체
   @param path  Load 할 Class의 상대 경로.
 */
static struct efile *
init_efile (r, path)
     struct rovm *r;
     char *path;
{
  char *fullpath;
  FILE *fp;
  size_t read_bytes;
  struct efile *ef;

  fullpath = efile_getfullpath (r, path, DEFAULT_ENVLANG_EXTENSION);
  if (!fullpath)
    return NULL;

  if (!(fp = fopen (fullpath, "rb")))
    {
      rovm_log (NULL, ROVMLOG_ERR, ROVMLOG_MARK, "fopen error.");
      rc_free (fullpath);
      return NULL;
    }

  if (fseek (fp, 0L, SEEK_END))
    {
      rovm_log (NULL, ROVMLOG_ERR, ROVMLOG_MARK, "fseek error.");
      rc_free (fullpath);
      fclose (fp);
      return NULL;
    }

  ef = (struct efile *) rc_malloc (sizeof (struct efile));
  EFILE_PATH (ef) = fullpath;
  EFILE_BUFLEN (ef) = (int) ftell (fp);
  EFILE_BUFBASE (ef) = EFILE_BUFCUR (ef) = (u1 *) rc_malloc (EFILE_BUFLEN (ef));

  fseek (fp, 0L, SEEK_SET);
  read_bytes = fread (EFILE_BUFBASE (ef), 1, EFILE_BUFLEN (ef), fp);
  if (read_bytes != EFILE_BUFLEN (ef))
    {
      rovm_log (NULL, ROVMLOG_ERR, ROVMLOG_MARK, "fread error.");
      rc_free (EFILE_BUFBASE (ef));
      rc_free (fullpath);
      rc_free (ef);
      fclose (fp);
      return NULL;
    }

  fclose (fp);

  return ef;
}

/**
   더 이상 사용하지 않는 struct efile 구조체를 해제합니다.

   @param ef    해제를 실시할 구조체.
 */
static void
finish_efile (ef)
     struct efile *ef;
{
  rc_free (EFILE_PATH (ef));
  rc_free (EFILE_BUFBASE (ef));
  rc_free (ef);
}

/**
   RvClass 구조체를 새롭게 할당하여 해당 포인터를 반환한다.
   
   @return      새롭게 할당된 RvClass 구조체.
 */
static RvClass *
init_eclass (r)
     struct rovm *r;
{
  RvClass *cls;

  cls = (RvClass *) rc_calloc (1, sizeof (RvClass));
  CLASS_ROVM (cls) = r;

  return cls;
}

/**
   RvField 를 파괴합니다.

   @param m     파괴할 field.
 */
static void
destroy_efield (f)
     RvField *f;
{
#define FIELDFREE(NODE)                         \
  if ((NODE))                                   \
    rc_free ((NODE))
  
  FIELDFREE (FIELD_NAME (f));
  FIELDFREE (FIELD_TYPE (f));

  rc_free (f); 
}

/**
   RvMethod 를 파괴합니다.

   @param m     파괴할 method.
 */
static void
destroy_emethod (m)
     RvMethod *m;
{
#define METHODFREE(NODE)                        \
  if ((NODE))                                   \
    rc_free ((NODE))
  
  METHODFREE (METHOD_NAME (m));
  if (METHOD_TYPE (m))
    {
      METHODFREE (MTYPE_STR (METHOD_TYPE (m)));
      METHODFREE (MTYPE_ARGV (METHOD_TYPE (m)));
      rc_free (METHOD_TYPE (m));
    }
  METHODFREE (METHOD_OP (m));
  rc_free (m); 
}

/**
   RvClass 구조체를 완전히 제거한다.

   @param cls   제거할 RvClass 구조체 포인터
 */
static void
destroy_eclass (cls)
     RvClass *cls;
{
#define CLSFREE(NODE) \
  if ((NODE))         \
    rc_free ((NODE))

  if (!cls)
    return;

  CLSFREE (CLASS_NAME (cls));

  if (CLASS_METHOD (cls))
    {
      RvMethod *next, *m = CLASS_METHOD (cls);

      while (m)
        {
          next = METHOD_NEXT (m);

          destroy_emethod (m);

          m = next;
        }
    }

  rc_free (cls);
}

/**
   클래스 CLS 의 field 중 NAME 과 TYPE 을 가지는 것을 찾아 반환한다.

   @param cls   클래스 포인터
   @param name  찾을 field 이름
   @param type  찾을 타입 이름
   @return      찾은 field 포인터, 발견하지 못하였을 경우, NULL_FIELD 을 반환합니다.
 */
RvField *
rc_find_efield (cls, name, type)
     RvClass *cls;
     u1 *name, *type;
{
  RvField *m;

  if (!cls || !name || !type)
    return NULL_FIELD;

  m = CLASS_FIELD (cls);

  while (m)
    {
      if (!strcmp (FIELD_NAME (m), name)
          && !strcmp (FIELD_TYPE (m), type))
        return m;

      m = FIELD_NEXT (m);
    }

  return NULL_FIELD;
}

/**
   클래스 CLS 의 메쏘드 중 NAME 과 TYPE 을 가지는 것을 찾아 반환한다.

   @param cls   클래스 포인터
   @param name  찾을 메쏘드 이름
   @param type  찾을 타입 이름
   @return      찾은 메쏘드, 발견하지 못하였을 경우, NULL_METHOD 을 반환합니다.
 */
RvMethod *
rc_find_emethod (cls, name, type)
     RvClass *cls;
     u1 *name, *type;
{
  RvMethod *m;

  if (!cls || !name || !type)
    return NULL_METHOD;

  m = CLASS_METHOD (cls);

  while (m)
    {
      if (!strcmp (METHOD_NAME (m), name)
          && METHOD_TYPE (m)
          && !strcmp (MTYPE_STR (METHOD_TYPE (m)), type))
        return m;

      m = METHOD_NEXT (m);
    }

  return NULL_METHOD;
}

/**
   Method Type 인 TYPE 을 해석하여 Argument 의 갯수를 반환한다.

   @param type  해석할 Method Type 문자열
   @return      해석한 argument 의 갯수
 */
u2
calctype_argc (path)
     u1 *path;
{
  int idx = 0;
  u1 *p = path;

  while (*p != '\0')
    {
      switch (*p)
        {
        case '[':
        case 'B':
        case 'C':
        case 'H':
        case 'I':
        case 'F':
        case 'D':
        case 'T':
        case 'S':
          idx++;
          break;
        case '(':
          break;
        case ')':
          return idx;
          break;
        default:
          rovm_log (NULL, ROVMLOG_ERR, ROVMLOG_MARK, "Can't handle type = `%c'", *p);
          break;
        }
      p++;
    }

  /* 여기 도달하면 오류이다.  */
  return 0xffff;
}

/**
   Method Type 인 TYPE 을 해석하여 각각의 Argument 의 offset 을 저장하여 해당
   배열을 반환하게 됩니다.

   @param type  해석할 Method Type 문자열
   @param argc  현재 Method Type 의 argument 갯수
   @return      해석한 argument 의 offset 을 담은 배열
 */
u2 *
calctype_argv (type, argc)
     u1 *type;
     u2 argc;
{
  int idx = 0;
  u1 *p = type;
  u2 *argv = (u2 *) rc_malloc (sizeof (u2) * argc);

  if (!argv || argc == 0xffff)
    return NULL;

  while (*p != '\0')
    {
      switch (*p)
        {
        case '[':
        case 'B':
        case 'C':
        case 'H':
        case 'I':
        case 'F':
        case 'D':
        case 'T':
        case 'S':
          argv[idx++] = (u2) (p - type);
          break;
        case '(':
          break;
        case ')':
          return argv;
          break;
        default:
          rovm_log (NULL, ROVMLOG_ERR, ROVMLOG_MARK, "Can't handle type = `%c'", *p);
          break;
        }
      p++;
    }

  /* 여기 도달하면 오류이다.  */
  rc_free (argv);

  return NULL;
}

/**
   Method Type 인 TYPE 을 해석하여 return 값이 있는 문자에 대한 offset 값을
   반환합니다.

   @param type  해석할 Method Type 문자열
   @return      해석한 return 문자의 offset 값.  만약 오류가 있다면 -1 을
                반환하게 됩니다.
 */
u2
calctype_ret (type)
     u1 *type;
{
  u1 *p = type;

  while (*p != '\0')
    {
      switch (*p)
        {
        case '[':
        case 'B':
        case 'C':
        case 'H':
        case 'I':
        case 'F':
        case 'D':
        case 'T':
        case 'S':
        case '(':
          break;
        case ')':
          return (p - type) + 1;
          break;
        default:
          rovm_log (NULL, ROVMLOG_ERR, ROVMLOG_MARK, "Can't handle type = `%c'", *p);
          break;
        }
      p++;
    }

  /* 여기 도달하면 오류이다.  */
  return 0xffff;
}

/**
   Method 의 Type 을 초기화할 때 사용되는 함수로써, TYPE 를 위한 공간을
   할당한 후 해당 타입을 해석하여 적절히 값을 설정합니다.

   @param type  해석할 type
   @return      새롭게 생성된 Method Type 구조
 */
static struct eclass_method_type *
init_emethodtype (type)
     u1 *type;
{
  struct eclass_method_type *mt;

  if (!type || (type && type[0] != '('))
    return NULL;

  mt = (struct eclass_method_type *) rc_malloc (sizeof (struct eclass_method_type));
  MTYPE_STR (mt) = type;
  MTYPE_ARGC (mt) = calctype_argc (type);
  if (MTYPE_ARGC (mt) == 0xffff)
    {
      rc_free (mt);
      return NULL;
    }
    
  MTYPE_ARGV (mt) = calctype_argv (type, MTYPE_ARGC (mt));
  if (!MTYPE_ARGV (mt))
    {
      rc_free (mt);
      return NULL;
    }

  MTYPE_RET (mt) = calctype_ret (type);
  if (MTYPE_RET (mt) == 0xffff)
    {
      rc_free (MTYPE_ARGV (mt));
      rc_free (mt);
      return NULL;
    }

  return mt;
}

/**
   RvField 구조체를 새롭게 할당하여 해당 포인터를 반환한다.
   
   @return      새롭게 할당된 RvField 구조체.
*/
RvField *
init_efield (cls, name, type)
     RvClass *cls;
     u1 *name, *type;
{
  RvField *f;

  f = (RvField *) rc_calloc (1, sizeof (RvField));
  FIELD_NAME (f) = name;
  FIELD_TYPE (f) = type;
  FIELD_INDEX (f) = CLASS_FIELD_COUNT (cls)++;

  return f;
}

/**
   RvMethod 구조체를 새롭게 할당하여 해당 포인터를 반환한다.
   
   @return      새롭게 할당된 RvMethod 구조체.
 */
RvMethod *
init_emethod (name, type)
     u1 *name, *type;
{
  RvMethod *m;

  m = (RvMethod *) rc_calloc (1, sizeof (RvMethod));
  METHOD_NAME (m) = name;
  METHOD_TYPE (m) = init_emethodtype (type);
  if (!METHOD_TYPE (m))
    {
      rc_free (m);
      return NULL_METHOD;
    }

  return m;
}

/** 속성이 ClassName 일 경우에 대한 type 값입니다.  */
#define ATTRIBUTE_CLASSNAME     0xff
/** 속성이 Method 일 경우에 대한 type 값입니다.  */
#define ATTRIBUTE_METHOD        0xfe
/** 속성이 MethodCode 일 경우에 대한 type 값입니다.  */
#define ATTRIBUTE_METHODCODE    0xfd
/** 속성이 Field 일 경우에 대한 type 값입니다.  */
#define ATTRIBUTE_FIELD         0xfc

/**
   EF 로부터 하나의 attribute 를 읽어 CLS 에 기록하게 된다.
 */
static int
rc_read_attribute (r, arg, ef)
     struct rovm *r;
     void *arg;
     struct efile *ef;
{
  int i;
  u1 attribute_type;
  u4 attribute_length;
  
  readu1 (ef, &attribute_type);
  readu4 (ef, &attribute_length);

  /*
    각 속성에 대한 자세한 설명은 http://envlang.kldp.net/ 홈페이지에 존재하는
    문서 `ENVLANG 파일 포맷' 를 참조하시기 바랍니다.
   */
  switch (attribute_type)
    {
    case ATTRIBUTE_CLASSNAME:
      {
        u1 name_len, *name;
        RvClass *cls = (RvClass *) arg;

        readu1 (ef, &name_len);
        name = (u1 *) rc_malloc (name_len + 1);
        readm (ef, name, name_len, 1);
        name[name_len] = '\0';

        if (CLASS_NAME (cls))
          {
            rc_free (name);
            goto error;
          }

        CLASS_NAME (cls) = name;
        break;
      }
    case ATTRIBUTE_FIELD:
      {
        u1 name_len, *name, *type;
        u2 type_len, attribute_counts;
        u2 RESERVED_SPACE;
        RvClass *cls = (RvClass *) arg;
        RvField *f;

        readu2 (ef, &RESERVED_SPACE);
        
        readu1 (ef, &name_len);
        name = (u1 *) rc_malloc (name_len + 1);
        readm (ef, name, name_len, 1);
        name[name_len] = '\0';

        readu2 (ef, &type_len);
        type = (u1 *) rc_malloc (type_len + 1);
        readm (ef, type, type_len, 1);
        type[type_len] = '\0';

        if (rc_find_efield (cls, name, type))
          {
            rc_free (type);
            rc_free (name);
            goto error;
          }

        f = init_efield (cls, name, type);
        if (!f)
          {
            rc_free (type);
            rc_free (name);
            goto error;
          }
        readu2 (ef, &attribute_counts);
        for (i=0; i<attribute_counts; i++)
          {
            if (rc_read_attribute (r, (void *) f, ef))
              {
                destroy_efield (f);
                goto error;
              }
          }

        FIELD_NEXT (f) = CLASS_FIELD (cls);
        CLASS_FIELD (cls) = f;
        break;
      }
    case ATTRIBUTE_METHOD:
      {
        u1 name_len, *name, *type;
        u2 type_len, attribute_counts;
        u4 RESERVED_SPACE;
        RvClass *cls = (RvClass *) arg;
        RvMethod *m;

        readu4 (ef, &RESERVED_SPACE);
        
        readu1 (ef, &name_len);
        name = (u1 *) rc_malloc (name_len + 1);
        readm (ef, name, name_len, 1);
        name[name_len] = '\0';

        readu2 (ef, &type_len);
        type = (u1 *) rc_malloc (type_len + 1);
        readm (ef, type, type_len, 1);
        type[type_len] = '\0';

        if (rc_find_emethod (cls, name, type))
          {
            rc_free (type);
            rc_free (name);
            goto error;
          }

        m = init_emethod (name, type);
        if (!m)
          {
            rc_free (type);
            rc_free (name);
            goto error;
          }
        readu2 (ef, &attribute_counts);
        for (i=0; i<attribute_counts; i++)
          {
            if (rc_read_attribute (r, (void *) m, ef))
              {
                destroy_emethod (m);
                goto error;
              }
          }

        METHOD_NEXT (m) = CLASS_METHOD (cls);
        CLASS_METHOD (cls) = m;
        break;
      }
    case ATTRIBUTE_METHODCODE:
      {
        rc_opcode_t *code;
        u2 attribute_counts, max_locals, RESERVED_SPACE;
        u4 code_length;
        RvMethod *m = (RvMethod *) arg;

        readu2 (ef, &RESERVED_SPACE);
        readu2 (ef, &max_locals);

        readu4 (ef, &code_length);
        code = (rc_opcode_t *) rc_malloc (code_length);
        readm (ef, code, code_length, 1);

        readu2 (ef, &RESERVED_SPACE);

        METHOD_MAX_LOCALS (m) = max_locals;
        METHOD_OPLEN (m) = code_length;
        METHOD_OP (m) = code;

        readu2 (ef, &attribute_counts);
        for (i=0; i<attribute_counts; i++)
          {
            if (rc_read_attribute (r, (void *) m, ef))
              goto error;
          }
        break;
      }
    default:
      rovm_log (NULL, ROVMLOG_ERR, ROVMLOG_MARK, "Invalid Attribute Type : 0x%x", attribute_type);
      return -1;
      /* not reach. */ 
      break;
    }

  return 0;

 error:
  return -1;
}

/**
   구성한 EF 구조체를 이용하여 하나씩 파일 포맷을 분석하여 읽는다.

   @param r     ROVM 구조체
   @param ef    struct efile 구조체.
 */
static RvClass *
rc_load_classfile_core (r, ef)
     struct rovm *r;
     struct efile *ef;
{
  int i;
  u2 fileformat_version, attribute_counts;
  u4 magic, made_time;
  RvClass *cls;
  
  readu4 (ef, &magic);
  if (magic != 0x0e0a0209)
    {
      rovm_log (NULL, ROVMLOG_ERR, ROVMLOG_MARK, "Invalid Magic Number");
      return NULL_CLASS;
    }

  readu2 (ef, &fileformat_version);
  if (fileformat_version != 0x0002)
    {
      rovm_log (NULL, ROVMLOG_ERR, ROVMLOG_MARK, "Only supports v0.2 file format version");
      return NULL_CLASS;
    }

  readu4 (ef, &made_time);

  cls = init_eclass (r);

  readu2 (ef, &attribute_counts);
  for (i=0; i<attribute_counts; i++)
    {
      if (rc_read_attribute (r, cls, ef))
        {
          destroy_eclass (cls);
          return NULL_CLASS;
        }
    }

  return cls;
}

/**
   PATH 에서 module 시작 함수의 이름을 구한 후 이를 반환한다.

   @param r     ROVM 구조체
   @param path  현재 Class 의 경로.
 */
char *
rc_getmodname (r, path)
     struct rovm *r;
     char *path;
{
  int i;
  char *ret, *p, *name;

  p = strrchr (path, '/');
  if (!p)
    return NULL;

  p++;
  name = rc_strdup (p);
  if (!name)
    return NULL;

  ret = (char *) rc_calloc (1, 5 + strlen (name) + 1);
  if (!ret)
    {
      rc_free (name);
      return NULL;
    }

  /* 만약 이름 P 에 '.' 과 같은 문자가 있을 경우, '_' 로 치환하여 준다.  */
  for (i=0; i<strlen (name); i++)
    {
      if (name[i] == '.')
        name[i] = '_';
    }

  strcat (ret, "init_");
  strcat (ret, name);

  rc_free (name);

  return ret;
}

/**
   Class 모듈 (ENVLANG 파일 포맷이 아니라, 동적 라이브러리 형태의 것) 을 로드하여 
   그에 대한 클래스 포인터를 반환합니다.
 */
RvClass *
rc_load_class_module (r, path)
     struct rovm *r;
     char *path;
{
  RvClass *cls;
  rc_dso_handle_t *modhandle;
  rc_dso_handle_sym_t modsym;
  int (*modfunc) (RvClass *);
  char *fullpath, *modname;

  fullpath = efile_getfullpath (r, path, DEFAULT_ENVLANG_NATIVE_EXTENSION);
  if (!fullpath)
    return NULL_CLASS;

  modname = rc_getmodname (r, path);
  if (!modname)
    {
      rc_free (fullpath);
      return NULL_CLASS;
    }

  /* 해당 파일을 ROVM 주소로 load 를 합니다.  */
  if (rc_dso_load (&modhandle, fullpath, ROVM_POOL (r)) != RC_SUCCESS) 
    {
      char *msg, my_error[256];

      msg = rc_pstrcat (ROVM_POOL (r), "Cannot load ", fullpath,
                        " into server: ",
                        rc_dso_error (modhandle, my_error, sizeof (my_error)),
                        NULL);

      rovm_log (NULL, ROVMLOG_ERR, ROVMLOG_MARK, "%s", msg);
      goto error;
    }

  rovm_log (NULL, ROVMLOG_DEBUG, ROVMLOG_MARK, "loaded module path `%s'", path);

  /* 이제 모듈의 시작 함수 포인터를 가져와서 해당 함수를 호출합니다.  */
  if (rc_dso_sym (&modsym, modhandle, modname) != RC_SUCCESS) 
    {
      char *msg, my_error[256];
      
      msg = rc_pstrcat (ROVM_POOL (r), "Can't locate API module function `",
                        modname, "' in file ", fullpath, ": ",
                        rc_dso_error (modhandle, my_error, sizeof(my_error)),
                        NULL);
      rovm_log (NULL, ROVMLOG_ERR, ROVMLOG_MARK, "%s", msg);
      goto error;
    }

  modfunc = (void *) modsym;  

  /* 이제 해당 함수를 실행하자.  */
  cls = init_eclass (r);
  CLASS_FLAG (cls) |= ACCESS_NATIVE;
  CLASS_NAME (cls) = rc_strdup (path);
  CLASS_NATIVE_HANDLE (cls) = modhandle;

  if (modfunc (cls))
    goto error;

  rc_free (fullpath);
  rc_free (modname);

  return cls;

 error:

  rc_free (fullpath);
  rc_free (modname);

  return NULL_CLASS;
}

/**
   Class 파일인 PATH 을 읽어서 로드하고, 그에 대한 결과값인 Class 포인터를
   반환하게 됩니다.

   만약 로드 과정에서 오류가 발생할 경우, NULL_CLASS 가 반환되게 됩니다.

   @param r     ROVM 구조체
   @param path  로드를 하게 될 파일의 경로.  이것은 *상대 경로* 형태이다.
 */
RvClass *
rc_load_classfile (r, path)
     struct rovm *r;
     char *path;
{
  RvClass *cls;
  struct efile *ef;

  if (!r || !path || (path && path[0] != '/'))
    return NULL_CLASS;

  if (rc_fileexist (r, path, DEFAULT_ENVLANG_EXTENSION))
    {
      ef = init_efile (r, path);
      if (!ef)
        return NULL_CLASS;
      
      cls = rc_load_classfile_core (r, ef);
      
      finish_efile (ef);
    }
  else if (rc_fileexist (r, path, DEFAULT_ENVLANG_NATIVE_EXTENSION))
    cls = rc_load_class_module (r, path);

  return cls;
}
