/*
 * Extension mechanism for the JavaScript virtual machine.
 * Copyright (c) 1999-2000 Cylant Technology, LLC
 *
 * Author: Brian Bassett <bbassett@bbassett.net>
 */

/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA
 */

/*
 * $Source: /home/cvs/entity/libentitynjs/ext.c,v $
 * $Id: ext.c,v 1.6 2000/07/25 05:26:00 imain Exp $
 */

#include "njs/internal.h"

#include <sys/stat.h>
#include <unistd.h>

/*
 * Directory handling functions.
 */

/* Directory cache linked list manipulation functions. */
int
js_ext_purge_extdir(JSVirtualMachine *vm)
{
  JSExtDirectoryList *thisPtr, *tempPtr;

  thisPtr = vm->dirlist;
  while(thisPtr != NULL)
    {
      tempPtr = thisPtr;
      thisPtr = thisPtr->next;
      js_free(tempPtr->dir);
      js_free(tempPtr);
    }

  vm->dirlist = NULL;
  return 1;
}

int
js_ext_resolve_modulename(JSVirtualMachine *vm, const char *module, char *buffer, int buffer_len)
{
  JSExtDirectoryList *dirs = vm->dirlist;
  struct stat stat_st;
  char *mod, *modl;
  int result;

  /* compute the normalized module name */
  /* I changed this from strdup to js_strdup, hopefully that's a good thing. */
  mod = js_strdup(vm, module);
  modl = mod;
  
  while(*modl != '\0')
    {
      if(*modl == '.')
	{
	  *modl = JS_HOST_DIR_SEP;
	}
      modl++;
    }
  
  /* Walk through the extension directories, looking for mod */
  while(dirs != NULL)
    {
      /* look for a libtool .la information file first*/
      js_snprintf (buffer, buffer_len, "%s%c%s.la", dirs->dir, JS_HOST_DIR_SEP, mod);
      result = stat(buffer, &stat_st);
      if(result == 0 && S_ISREG(stat_st.st_mode))
	{
	  /* found mod.la */
	  return JS_EXT_LIBTOOL;
	}

      /* look for bytecode file second */
      js_snprintf (buffer, buffer_len, "%s%c%s.jsc", dirs->dir, JS_HOST_DIR_SEP, mod);
      result = stat(buffer, &stat_st);
      if(result == 0 && S_ISREG(stat_st.st_mode))
	{
	  /* found mod.jsc */
	  return JS_EXT_BYTECODE;
	}

      /* look for JavaScript source file */
      js_snprintf (buffer, buffer_len, "%s%c%s.js", dirs->dir, JS_HOST_DIR_SEP, mod);
      result = stat(buffer, &stat_st);
      if(result == 0 && S_ISREG(stat_st.st_mode))
	{
	  /* found mod.js */
	  return JS_EXT_SOURCE;
	}

      /* didn't find either, try the next directory */
      dirs = dirs->next;
    }

  /* Didn't find anything resembling mod */
  return JS_EXT_UNRESOLVED;
}

int
js_ext_add_loadedmodule(JSVirtualMachine *vm, const char *module)
{
  JSLoadedModules *link;

  link = js_malloc(vm, sizeof(JSLoadedModules));
  link->next = vm->modules;
  link->module = js_strdup(vm, module);

  vm->modules = link;

  return 1;
}

int
js_ext_purge_loadedmodule(JSVirtualMachine *vm)
{
  JSLoadedModules *thisPtr, *tempPtr;

  thisPtr = vm->modules;
  while(thisPtr != NULL)
    {
      tempPtr = thisPtr;
      thisPtr = thisPtr->next;
      js_free(tempPtr->module);
      js_free(tempPtr);
    }

  vm->modules = NULL;

  return 1;
}

int
js_ext_module_loaded(JSVirtualMachine *vm, const char *module)
{
  JSLoadedModules *thisPtr;

  thisPtr = vm->modules;
  while(thisPtr != NULL)
    {
      if(strcmp(thisPtr->module, module) == 0)
        {
          /* Found it! */
          return 1;
        }

      thisPtr = thisPtr->next;
    }

  /* Didn't find it... :( */
  return 0;
}

int
js_ext_vm_load_module(JSVirtualMachine *vm, const char *module)
{
  int result;
  char filename[1024];
  FILE *file;
  JSByteCode *bc;
  JSNode source;
  JSNode args[5];

  /* See if we've already loaded the module in question. */
  result = js_ext_module_loaded(vm, module);
  if(result == 1)
    {
      /* We've already loaded this module. Silently return success. */
      return 1;
    }

  /* Resolve the name and type of the module. */
  result = js_ext_resolve_modulename(vm, module, filename, sizeof (filename));
  switch(result)
    {
      case JS_EXT_BYTECODE:
	  /* Found the module as bytecode. */
	  file = fopen(filename, "rb");
	  if(file == NULL)
	    {
	      js_vm_set_err (vm, "VM: cannot open byte-code file \"%s\": %s",
		      filename, strerror (errno));
	      return 0;
	    }
	  
	  bc = js_bc_read_file(file);
	  fclose(file);
	  if(bc == NULL)
	    {
	      /* XXX Error message */
	      return 0;
	    }

	  break;

      case JS_EXT_SOURCE:
	  /* Found the module as source code. */
	  js_vm_make_string (vm, &source, filename, strlen (filename));
	  
	  /* Let's compile the code. */
	  args[0].type = JS_INTEGER;	/* Argument count. */
	  args[0].u.vinteger = 4;
	  JS_COPY (&args[1], &source);	/* Source to compiler. */
	  args[2].type = JS_INTEGER;	/* Flags. */
	  args[2].u.vinteger = 0;
	  args[2].u.vinteger |= JSC_FLAG_GENERATE_DEBUG_INFO;
	  args[2].u.vinteger |= JSC_FLAG_OPTIMIZE_PEEPHOLE;
	  args[2].u.vinteger |= JSC_FLAG_OPTIMIZE_JUMPS;
	  args[2].u.vinteger |= JSC_FLAG_WARN_WITH_CLOBBER;
	  args[3].type = JS_NULL;	/* Assembler file. */
	  args[4].type = JS_NULL;	/* Byte-code file. */

	  /* Call the compiler entry point. */
	  result = js_vm_apply (vm, "JSC$compile_file", NULL, 5, args);
	  if (result == 0)
	    return 0;

	  /*
	   * The resulting byte-code file is now at vm->exec_result.
	   *
	   * Note!  The byte-code is a string allocated form the vm heap.
	   * The garbage collector can free it when it wants since the result
	   * isn't protected.  However, we have no risk here because we
	   * first convert the byte-code data block to our internal
	   * JSByteCode block that shares no memory with the original data.
	   */

	  assert (vm->exec_result.type == JS_STRING);

	  bc = js_bc_read_data (vm->exec_result.u.vstring->data,
	                        vm->exec_result.u.vstring->len);

	  break;

      default:
	  js_vm_set_err (vm, "VM: cannot resolve module %s", module);
	  return 0;
    }

    result = js_vm_execute (vm, bc);
    js_bc_free (bc);

    /* Add the module to the loaded module cache. */
    js_ext_add_loadedmodule (vm, module);
    
    return result;
}

int
js_ext_vm_load_module_from_symbol(JSVirtualMachine *vm, JSSymbol sym)
{
  return js_ext_vm_load_module(vm, js_vm_symname(vm, sym));
}

/* Public functions. */

int
js_ext_add_directory(JSInterpPtr interp, const char *dir)
{
  struct stat stat_st;
  JSExtDirectoryList *thisPtr;
    
  /* Check to make sure dir exists and is a directory. */
  if(stat (dir, &stat_st) != 0)
    {
      /* error condition */
      js_vm_set_err (interp->vm, "VM: Error with extension dir: %s", strerror(errno));
      return 0;
    }

  if(!S_ISDIR(stat_st.st_mode))
    {
      /* not a dir */
      js_vm_set_err (interp->vm, "VM: Directory not a directory: %s", dir);
      return 0;
    }

  /* Search through cache to see if dir is already in cache. */
  thisPtr = interp->vm->dirlist;
  while (thisPtr != NULL)
    {
      if(strcmp (thisPtr->dir, dir) == 0)
        {
          /* We have already added this dir, return success. */
          return 1;
        }

      thisPtr = thisPtr->next;
    }

  /* Add directory to cache. */
  thisPtr = js_malloc (interp->vm, sizeof(JSExtDirectoryList));
  
  thisPtr->dir = js_strdup (interp->vm, dir);
  thisPtr->next = interp->vm->dirlist;
  
  interp->vm->dirlist = thisPtr;

  return 1;	/* Success! */
}

int
js_ext_remove_directory(JSInterpPtr interp, const char *dir)
{
  JSExtDirectoryList *thisPtr, *prevPtr, *tempPtr;

  /* Die a flaming death if no directories in cache. */
  if (interp->vm->dirlist == NULL)
    {
      js_vm_set_err (interp->vm, "VM: No directories in cache.");
      return 0;
    }
  
  /* Loop through vm->dirlist to find dir. */
  if (strcmp (interp->vm->dirlist->dir, dir) == 0)
    {
      /* We found dir at the head. */
      tempPtr = interp->vm->dirlist;
      interp->vm->dirlist = interp->vm->dirlist->next;
      js_free (tempPtr->dir);
      js_free (tempPtr);
      return 1;	/* Success! */
    }
  else
    {
      /* Loop through to find it. */
      prevPtr = interp->vm->dirlist;
      thisPtr = interp->vm->dirlist->next;

      while(thisPtr != NULL && strcmp(thisPtr->dir, dir) != 0)
        {
          prevPtr = thisPtr;
          thisPtr = thisPtr->next;
        }

      if (thisPtr != NULL)
        {
          tempPtr = thisPtr;
          prevPtr->next = thisPtr->next;
          js_free (tempPtr->dir);
          js_free (tempPtr);
          return 1;	/* Success! */
        }
    }

  js_vm_set_err (interp->vm, "VM: Directory not in extension path: %s", dir);
  return 0;
}

int
js_ext_default_directories (JSInterpPtr interp)
{
  int result;
  result = js_ext_add_directory (interp, JS_EXT_DIR);
  if(result == 0)
    return 0;

#ifdef JS_SITE_EXT_DIR
  result = js_ext_add_directory (interp, JS_SITE_EXT_DIR);
  if(result == 0)
    return 0;
#endif

  return 1;
}

int
js_ext_load_module (JSInterpPtr interp, const char *name)
{
  return js_ext_vm_load_module (interp->vm, name);
}




syntax highlighted by Code2HTML, v. 0.9.1