/*
 * The builtin File object.
 * Copyright (c) 1998-1999 New Generation Software (NGS) Oy
 *
 * Author: Markku Rossi <mtr@ngs.fi>
 */

/*
 * 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: /tmp/cvsroot-15-2-2007/clamav-devel/libclamav/js/b_file.c,v $
 * $Id: b_file.c,v 1.2 2006/10/28 11:27:44 njh Exp $
 */

/*
 * Static methods.
 *
 *   byteToString (BYTE) => string
 * + chmod (string, int) => boolean
 * + lstat (PATH) => array / boolean
 * + remove (PATH) => boolean
 * + rename (FROM, TO) => boolean
 * + stat (PATH) => array / boolean
 *   stringToByte (STRING) => number
 *
 * Methods:
 *
 *   open (MODE) => boolean
 *   close () => boolean
 *   setPosition (POSITION [, WHENCE]) => boolean
 *   getPosition () => integer
 *   eof () => boolean
 *   read (SIZE) => string
 *   readln () => string
 *   readByte () => integer
 *   write (STRING) => boolean
 *   writeln (STRING) => boolean
 *   writeByte (INTEGER) => boolean
 * + ungetByte (BYTE) => boolean
 *   flush () => boolean
 *   getLength () => integer
 *   exists () => boolean
 *   error () => integer
 *   clearError () => true
 *
 * Properties:
 *
 *   autoFlush	boolean		mutable
 *   bufferSize	integer		mutable
 */
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif

#ifdef	CL_EXPERIMENTAL

#include "jsint.h"

#include <sys/stat.h>

/*
 * Types and definitions.
 */

#define INSECURE()		\
  do {				\
    if (secure_mode)		\
      goto insecure_feature;	\
  } while (0)

/* Class context. */
struct file_ctx_st
{
  /* Static methods. */
  JSSymbol s_byteToString;
  JSSymbol s_chmod;
  JSSymbol s_lstat;
  JSSymbol s_remove;
  JSSymbol s_rename;
  JSSymbol s_stat;
  JSSymbol s_stringToByte;

  /* Methods */
  JSSymbol s_open;
  JSSymbol s_close;
  JSSymbol s_setPosition;
  JSSymbol s_getPosition;
  JSSymbol s_eof;
  JSSymbol s_read;
  JSSymbol s_readln;
  JSSymbol s_readByte;
  JSSymbol s_write;
  JSSymbol s_writeln;
  JSSymbol s_writeByte;
  JSSymbol s_ungetByte;
  JSSymbol s_flush;
  JSSymbol s_getLength;
  JSSymbol s_exists;
  JSSymbol s_error;
  JSSymbol s_clearError;

  /* Properties. */
  JSSymbol s_autoFlush;
  JSSymbol s_bufferSize;
};

typedef struct file_ctx_st FileCtx;

/* Instance context. */
struct file_instance_ctx_st
{
  /* Flags. */
  unsigned int dont_close : 1;

  char *path;
  JSIOStream *stream;

  /* Needed for the delete_proc. */
  JSVirtualMachine *vm;
};

typedef struct file_instance_ctx_st FileInstanceCtx;


/*
 * Static functions.
 */

/* Method proc. */
static int
method (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
	void *instance_context, JSSymbol method, JSNode *result_return,
	JSNode *args)
{
  FileCtx *ctx = builtin_info->obj_context;
  FileInstanceCtx *ictx = instance_context;
  char buf[256];
  long int li = 0;
  int i = 0;
  char *cp;
  int secure_mode = vm->security & JS_VM_SECURE_FILE;

  /* The default result is false. */
  result_return->type = JS_BOOLEAN;
  result_return->u.vboolean = 0;

  /*
   * Static methods.
   */
  if (method == ctx->s_byteToString)
    {
      if (args->u.vinteger != 1)
	goto argument_error;

      i = -1;
      if (args[1].type == JS_INTEGER)
	{
	  i = args[1].u.vinteger;
	  if (i < 0 || i > 255)
	    i = -1;
	}

      js_vm_make_string (vm, result_return, NULL, 1);

      if (i < 0)
	result_return->u.vstring->len = 0;
      else
	result_return->u.vstring->data[0] = i;
    }
  /* ********************************************************************** */
  else if (method == ctx->s_chmod)
    {
      INSECURE ();

      if (args->u.vinteger != 2)
	goto argument_error;
      if (args[1].type != JS_STRING)
	goto argument_type_error;
      if (args[2].type != JS_INTEGER)
	goto argument_type_error;

      result_return->type= JS_BOOLEAN;

      cp = js_string_to_c_string (vm, &args[1]);
      result_return->u.vboolean = (chmod (cp, args[2].u.vinteger) == 0);
      js_free (cp);
    }
  /* ********************************************************************** */
  else if (method == ctx->s_lstat || method == ctx->s_stat)
    {
      char *path;
      struct stat stat_st;
      int result;

      INSECURE ();

      if (args->u.vinteger != 1)
	goto argument_error;

      path = js_string_to_c_string (vm, &args[1]);

#if HAVE_LSTAT
      if (method == ctx->s_lstat)
	result = lstat (path, &stat_st);
      else
#endif /* HAVE_LSTAT */
	result = stat (path, &stat_st);

      js_free (path);

      if (result >= 0)
	{
	  JSNode *node;

	  /* Success. */
	  js_vm_make_array (vm, result_return, 13);
	  node = result_return->u.varray->data;

	  /* dev */
	  node->type = JS_INTEGER;
	  node->u.vinteger = stat_st.st_dev;
	  node++;

	  /* ino */
	  node->type = JS_INTEGER;
	  node->u.vinteger = stat_st.st_ino;
	  node++;

	  /* mode */
	  node->type = JS_INTEGER;
	  node->u.vinteger = stat_st.st_mode;
	  node++;

	  /* nlink */
	  node->type = JS_INTEGER;
	  node->u.vinteger = stat_st.st_nlink;
	  node++;

	  /* uid */
	  node->type = JS_INTEGER;
	  node->u.vinteger = stat_st.st_uid;
	  node++;

	  /* gid */
	  node->type = JS_INTEGER;
	  node->u.vinteger = stat_st.st_gid;
	  node++;

	  /* rdev */
	  node->type = JS_INTEGER;
	  node->u.vinteger = stat_st.st_rdev;
	  node++;

	  /* size */
	  node->type = JS_INTEGER;
	  node->u.vinteger = stat_st.st_size;
	  node++;

	  /* atime */
	  node->type = JS_INTEGER;
	  node->u.vinteger = stat_st.st_atime;
	  node++;

	  /* mtime */
	  node->type = JS_INTEGER;
	  node->u.vinteger = stat_st.st_mtime;
	  node++;

	  /* ctime */
	  node->type = JS_INTEGER;
	  node->u.vinteger = stat_st.st_ctime;
	  node++;

	  /* blksize */
	  node->type = JS_INTEGER;
#if HAVE_STAT_ST_ST_BLKSIZE
	  node->u.vinteger = stat_st.st_blksize;
#else /* not HAVE_STAT_ST_ST_BLKSIZE */
	  node->u.vinteger = 0;
#endif /* not HAVE_STAT_ST_ST_BLKSIZE */
	  node++;

	  /* blocks */
	  node->type = JS_INTEGER;
#if HAVE_STAT_ST_ST_BLOCKS
	  node->u.vinteger = stat_st.st_blocks;
#else /* not HAVE_STAT_ST_ST_BLOCKS */
	  node->u.vinteger = 0;
#endif /* not HAVE_STAT_ST_ST_BLOCKS */
	}
    }
  /* ********************************************************************** */
  else if (method == ctx->s_remove)
    {
      char *path;

      INSECURE ();

      if (args->u.vinteger != 1)
	goto argument_error;

      if (args[1].type != JS_STRING)
	goto argument_type_error;

      path = js_string_to_c_string (vm, &args[1]);
      i = remove (path);
      js_free (path);

      result_return->u.vboolean = (i == 0);
    }
  /* ********************************************************************** */
  else if (method == ctx->s_rename)
    {
      char *path1;
      char *path2;

      INSECURE ();

      if (args->u.vinteger != 2)
	goto argument_error;

      if (args[1].type != JS_STRING || args[2].type != JS_STRING)
	goto argument_type_error;

      path1 = js_string_to_c_string (vm, &args[1]);
      path2 = js_string_to_c_string (vm, &args[2]);

      i = rename (path1, path2);

      js_free (path1);
      js_free (path2);

      result_return->u.vboolean = (i == 0);
    }
  /* ********************************************************************** */
  else if (method == ctx->s_stringToByte)
    {
      if (args->u.vinteger != 1)
	goto argument_error;

      result_return->type = JS_INTEGER;

      if (args[1].type == JS_STRING && args[1].u.vstring->len > 0)
	result_return->u.vinteger = args[i].u.vstring->data[0];
      else
	result_return->u.vinteger = 0;
    }
  /* ********************************************************************** */
  else if (method == vm->syms.s_toString)
    {
      if (args->u.vinteger != 0)
	goto argument_error;

      if (ictx)
	js_vm_make_string (vm, result_return, ictx->path, strlen (ictx->path));
      else
	js_vm_make_static_string (vm, result_return, "File", 4);
    }
  /* ********************************************************************** */
  else if (ictx)
    {
      /*
       * Instance methods.
       */

      if (method == ctx->s_open)
	{
	  int readp = 0;
	  int writep = 0;

	  INSECURE ();

	  if (args->u.vinteger != 1)
	    goto argument_error;
	  if (args[1].type != JS_STRING
	      || args[1].u.vstring->len == 0
	      || args[1].u.vstring->len > 3)
	    goto argument_type_error;

	  i = args[1].u.vstring->len;
	  memcpy (buf, args[1].u.vstring->data, i);

	  if (buf[i - 1] != 'b')
	    buf[i++] = 'b';
	  buf[i] = '\0';

	  /* Check that the mode is valid. */
	  if (strcmp (buf, "rb") == 0)
	    readp = 1;
	  else if (strcmp (buf, "wb") == 0)
	    writep = 1;
	  else if (strcmp (buf, "ab") == 0)
	    writep = 1;
	  else if (strcmp (buf, "r+b") == 0)
	    readp = writep = 1;
	  else if (strcmp (buf, "w+b") == 0)
	    readp = writep = 1;
	  else if (strcmp (buf, "a+b") == 0)
	    readp = writep = 1;
	  else
	    {
	      sprintf (vm->error, "File.%s(): illegal open mode \"%s\"",
		       js_vm_symname (vm, method), buf);
	      js_vm_error (vm);
	    }

	  if (ictx->stream == NULL)
	    {
	      /* Do open. */
	      JS_VM_ALLOCATE_FD (vm, "File.open()");
	      ictx->stream = js_iostream_file (fopen (ictx->path, buf), readp,
					       writep, 1);
	      if (ictx->stream == NULL)
		JS_VM_FREE_FD (vm);
	      else
		result_return->u.vboolean = 1;
	    }
	}
      /* ***************************************************************** */
      else if (method == ctx->s_close)
	{
	  if (ictx->stream != NULL)
	    {
	      int result = 0;

	      if (!ictx->dont_close)
		{
		  result = js_iostream_close (ictx->stream);
		  JS_VM_FREE_FD (vm);
		}

	      ictx->stream = NULL;
	      result_return->u.vboolean = result >= 0;
	    }
	}
      /* ***************************************************************** */
      else if (method == ctx->s_setPosition)
	{
	  if (args->u.vinteger == 1)
	    {
	      if (args[1].type != JS_INTEGER)
		goto argument_type_error;
	      li = args[1].u.vinteger;
	      i = SEEK_SET;
	    }
	  else if (args->u.vinteger == 2)
	    {
	      if (args[2].type == JS_INTEGER)
		{
		  switch (args[2].u.vinteger)
		    {
		    case 1:
		      i = SEEK_CUR;
		      break;

		    case 2:
		      i = SEEK_END;
		      break;

		    default:
		      i = SEEK_SET;
		      break;
		    }
		}
	      else
		i = SEEK_SET;
	    }
	  else
	    goto argument_error;

	  if (ictx->stream && js_iostream_seek (ictx->stream, li, i) >= 0)
	    result_return->u.vboolean = 1;
	}
      /* ***************************************************************** */
      else if (method == ctx->s_getPosition)
	{
	  if (args->u.vinteger != 0)
	    goto argument_error;

	  result_return->type = JS_INTEGER;
	  if (ictx->stream == NULL)
	    result_return->u.vinteger = -1;
	  else
	    result_return->u.vinteger
	      = js_iostream_get_position (ictx->stream);
	}
      /* ***************************************************************** */
      else if (method == ctx->s_eof)
	{
	  if (args->u.vinteger != 0)
	    goto argument_error;

	  if (ictx->stream != NULL)
	    result_return->u.vboolean = ictx->stream->at_eof;
	}
      /* ***************************************************************** */
      else if (method == ctx->s_read)
	{
	  size_t got;
	  char *buffer;

	  if (args->u.vinteger != 1)
	    goto argument_error;
	  if (args[1].type != JS_INTEGER || args[1].u.vinteger < 0)
	    goto argument_type_error;

	  if (ictx->stream != NULL)
	    {
	      buffer = js_vm_alloc (vm, args[1].u.vinteger + 1);

	      got = js_iostream_read (ictx->stream, buffer,
				      args[1].u.vinteger);
	      if (got < 0)
		got = 0;

	      js_vm_make_static_string (vm, result_return, buffer, got);
	      result_return->u.vstring->staticp = 0;
	    }
	}
      /* ***************************************************************** */
      else if (method == ctx->s_readln)
	{
	  int ch;
	  unsigned int bufpos = 0;
	  unsigned int buflen = 0;
	  char *buffer = NULL;

	  if (args->u.vinteger != 0)
	    goto argument_error;

	  if (ictx->stream != NULL)
	    {
	      /* Flush all buffered output data. */
	      js_iostream_flush (ictx->stream);

	      while (1)
		{
		  /* Process all the data we have in the buffer. */
		  for (; ictx->stream->bufpos < ictx->stream->data_in_buf
			 && (ictx->stream->buffer[ictx->stream->bufpos]
			     != '\n');
		       ictx->stream->bufpos++)
		    {
		      if (bufpos >= buflen)
			{
			  buflen += 1024;
			  buffer = js_vm_realloc (vm, buffer, buflen);
			}
		      buffer[bufpos++]
			= ictx->stream->buffer[ictx->stream->bufpos];
		    }

		  if (ictx->stream->bufpos >= ictx->stream->data_in_buf)
		    {
		      int result;

		      /* Read past the buffer. */
		      if (ictx->stream->at_eof)
			/* EOF seen. */
			break;

		      /* Read more data. */
		      js_iostream_fill_buffer (ictx->stream);
		    }
		  else
		    {
		      /* Got it.  Skip the newline character. */
		      ictx->stream->bufpos++;
		      break;
		    }
		}

	      /* Remove '\r' characters. */
	      while (bufpos > 0)
		if (buffer[bufpos - 1] == '\r')
		  bufpos--;
		else
		  break;

	      if (buffer == NULL)
		/* An empty string.  Allocate one byte. */
		buffer = js_vm_alloc (vm, 1);

	      /*
	       * Use the data we already had.  In maximum, it has only
	       * 1023 bytes overhead.
	       */
	      js_vm_make_static_string (vm, result_return, buffer, bufpos);
	      result_return->u.vstring->staticp = 0;
	    }
	}
      /* ***************************************************************** */
      else if (method == ctx->s_readByte)
	{
	  result_return->type = JS_INTEGER;
	  if (ictx->stream == NULL)
	    result_return->u.vinteger = -1;
	  else
	    {
	    retry:
	      if (ictx->stream->bufpos < ictx->stream->data_in_buf)
		result_return->u.vinteger
		  = ictx->stream->buffer[ictx->stream->bufpos++];
	      else
		{
		  if (ictx->stream->at_eof)
		    result_return->u.vinteger = -1;
		  else
		    {
		      js_iostream_fill_buffer (ictx->stream);
		      goto retry;
		    }
		}
	    }
	}
      /* ***************************************************************** */
      else if (method == ctx->s_write || method == ctx->s_writeln)
	{
	  size_t wrote;
	  int autoflush;

	  if (args->u.vinteger != 1)
	    goto argument_error;
	  if (args[1].type != JS_STRING)
	    goto argument_type_error;

	  if (ictx->stream != NULL)
	    {
	      autoflush = ictx->stream->autoflush;
	      ictx->stream->autoflush = 0;

	      wrote = js_iostream_write (ictx->stream,
					 args[1].u.vstring->data,
					 args[1].u.vstring->len);
	      if (wrote == args[1].u.vstring->len)
		{
		  /* Success. */
		  result_return->u.vboolean = 1;

		  if (method == ctx->s_writeln)
		    if (js_iostream_write (ictx->stream,
					   JS_HOST_LINE_BREAK,
					   JS_HOST_LINE_BREAK_LEN) < 0)
		      /* No, it was not a success. */
		      result_return->u.vboolean = 0;
		}

	      ictx->stream->autoflush = autoflush;
	      if (autoflush)
		js_iostream_flush (ictx->stream);
	    }
	}
      /* ***************************************************************** */
      else if (method == ctx->s_writeByte)
	{
	  unsigned char buf[1];

	  if (args->u.vinteger != 1)
	    goto argument_error;
	  if (args[1].type != JS_INTEGER)
	    goto argument_type_error;

	  buf[0] = args[1].u.vinteger;

	  if (ictx->stream != NULL)
	    result_return->u.vboolean
	      = js_iostream_write (ictx->stream, buf, 1) >= 0;
	}
      /* ***************************************************************** */
      else if (method == ctx->s_ungetByte)
	{
	  if (args->u.vinteger != 1)
	    goto argument_error;
	  if (args[1].type != JS_INTEGER)
	    goto argument_type_error;

	  if (ictx->stream != NULL)
	    result_return->u.vboolean
	      = js_iostream_unget (ictx->stream, args[1].u.vinteger);
	}
      /* ***************************************************************** */
      else if (method == ctx->s_flush)
	{
	  if (args->u.vinteger != 0)
	    goto argument_error;

	  if (ictx->stream != NULL && js_iostream_flush (ictx->stream) >= 0)
	    result_return->u.vboolean = 1;
	}
      /* ***************************************************************** */
      else if (method == ctx->s_getLength)
	{
	  if (args->u.vinteger != 0)
	    goto argument_error;

	  /* The default error code is an integer -1. */
	  result_return->type = JS_INTEGER;
	  result_return->u.vinteger = -1;

	  if (ictx->stream != NULL)
	    result_return->u.vinteger
	      = js_iostream_get_length (ictx->stream);
	}
      /* ***************************************************************** */
      else if (method == ctx->s_exists)
	{
	  if (args->u.vinteger != 0)
	    goto argument_error;

	  if (ictx->stream)
	    {
	      /* Since we have opened the file, it must exist. */
	      result_return->u.vboolean = 1;
	    }
	  else
	    {
	      struct stat stat_st;

	      if (stat (ictx->path, &stat_st) >= 0)
		result_return->u.vboolean = 1;
	    }
	}
      /* ***************************************************************** */
      else if (method == ctx->s_error)
	{
	  if (args->u.vinteger != 0)
	    goto argument_error;

	  result_return->type = JS_INTEGER;
	  if (ictx->stream == NULL)
	    result_return->u.vinteger = -1;
	  else
	    result_return->u.vinteger = ictx->stream->error;
	}
      /* ***************************************************************** */
      else if (method == ctx->s_clearError)
	{
	  if (args->u.vinteger != 0)
	    goto argument_error;

	  if (ictx->stream != NULL)
	    {
	      ictx->stream->error = 0;
	      result_return->u.vboolean = 1;
	    }
	}
      /* ***************************************************************** */
      else
	return JS_PROPERTY_UNKNOWN;
    }
  else
    return JS_PROPERTY_UNKNOWN;

  return JS_PROPERTY_FOUND;


  /*
   * Error handling.
   */

 argument_error:
  sprintf (vm->error, "File.%s(): illegal amount of arguments",
	   js_vm_symname (vm, method));
  js_vm_error (vm);

 argument_type_error:
  sprintf (vm->error, "File.%s(): illegal argument",
	   js_vm_symname (vm, method));
  js_vm_error (vm);

 insecure_feature:
  sprintf (vm->error, "File.%s(): not allowed in secure mode",
	   js_vm_symname (vm, method));
  js_vm_error (vm);

  /* NOTREACHED */
  return 0;
}

/* Property proc. */
static int
property (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
	  void *instance_context, JSSymbol property, int set, JSNode *node)
{
  FileCtx *ctx = builtin_info->obj_context;
  FileInstanceCtx *ictx = instance_context;

  if (ictx)
    {
      /* Instance properties. */
      if (property == ctx->s_autoFlush)
	{
	  if (ictx->stream == NULL)
	    goto not_open;

	  if (set)
	    {
	      if (node->type != JS_BOOLEAN)
		goto argument_type_error;

	      ictx->stream->autoflush = node->u.vboolean;
	    }
	  else
	    {
	      node->type = JS_BOOLEAN;
	      node->u.vboolean = ictx->stream->autoflush;
	    }
	}
      /* ***************************************************************** */
      else if (property == ctx->s_bufferSize)
	{
	  if (ictx->stream == NULL)
	    goto not_open;

	  if (set)
	    {
	      unsigned char *buf;
	      unsigned int len;

	      if (node->type != JS_INTEGER)
		goto argument_type_error;

	      js_iostream_flush (ictx->stream);

	      len = node->u.vinteger;
	      buf = js_realloc (vm, ictx->stream->buffer, len);

	      ictx->stream->buflen = len;
	      ictx->stream->buffer = buf;
	    }
	  else
	    {
	      node->type = JS_INTEGER;
	      node->u.vinteger = ictx->stream->buflen;
	    }
	}
      /* ***************************************************************** */
      else
	{
	  if (!set)
	    node->type = JS_UNDEFINED;

	  return JS_PROPERTY_UNKNOWN;
	}
    }
  else
    {
      if (!set)
	node->type = JS_UNDEFINED;

      return JS_PROPERTY_UNKNOWN;
    }

  return JS_PROPERTY_FOUND;


  /* Error handling. */

 argument_type_error:
  sprintf (vm->error, "File.%s: illegal value",
	   js_vm_symname (vm, property));
  js_vm_error (vm);

 not_open:
  sprintf (vm->error, "File.%s: the stream is not opened",
	   js_vm_symname (vm, property));
  js_vm_error (vm);

  /* NOTREACHED */
  return 0;
}


/* New proc. */
static void
new_proc (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info, JSNode *args,
	  JSNode *result_return)
{
  FileInstanceCtx *instance;

  if (args->u.vinteger != 1)
    {
      sprintf (vm->error, "new File(): illegal amount of arguments");
      js_vm_error (vm);
    }
  if (args[1].type != JS_STRING)
    {
      sprintf (vm->error, "new File(): illegal argument");
      js_vm_error (vm);
    }

  instance = js_calloc (vm, 1, sizeof (*instance));
  instance->path = js_string_to_c_string (vm, &args[1]);
  instance->vm = vm;

  js_vm_builtin_create (vm, result_return, builtin_info, instance);
}

/* Delete proc. */
static void
delete_proc (JSBuiltinInfo *builtin_info, void *instance_context)
{
  FileInstanceCtx *ictx = instance_context;

  if (ictx)
    {
      if (ictx->stream)
	{
	  if (!ictx->dont_close)
	    {
	      js_iostream_close (ictx->stream);
	      JS_VM_FREE_FD (ictx->vm);
	    }

	  ictx->stream = NULL;
	}

      js_free (ictx->path);
      js_free (ictx);
    }
}

/*
 * Global functions.
 */

void
js_builtin_File (JSVirtualMachine *vm)
{
  JSNode *n;
  JSBuiltinInfo *info;
  FileCtx *ctx;

  ctx = js_calloc (vm, 1, sizeof (*ctx));

  ctx->s_byteToString	= js_vm_intern (vm, "byteToString");
  ctx->s_chmod		= js_vm_intern (vm, "chmod");
  ctx->s_lstat		= js_vm_intern (vm, "lstat");
  ctx->s_remove		= js_vm_intern (vm, "remove");
  ctx->s_rename		= js_vm_intern (vm, "rename");
  ctx->s_stat		= js_vm_intern (vm, "stat");
  ctx->s_stringToByte	= js_vm_intern (vm, "stringToByte");

  ctx->s_open		= js_vm_intern (vm, "open");
  ctx->s_close		= js_vm_intern (vm, "close");
  ctx->s_setPosition	= js_vm_intern (vm, "setPosition");
  ctx->s_getPosition	= js_vm_intern (vm, "getPosition");
  ctx->s_eof		= js_vm_intern (vm, "eof");
  ctx->s_read		= js_vm_intern (vm, "read");
  ctx->s_readln		= js_vm_intern (vm, "readln");
  ctx->s_readByte	= js_vm_intern (vm, "readByte");
  ctx->s_write		= js_vm_intern (vm, "write");
  ctx->s_writeln	= js_vm_intern (vm, "writeln");
  ctx->s_writeByte	= js_vm_intern (vm, "writeByte");
  ctx->s_ungetByte	= js_vm_intern (vm, "ungetByte");
  ctx->s_flush		= js_vm_intern (vm, "flush");
  ctx->s_getLength	= js_vm_intern (vm, "getLength");
  ctx->s_exists		= js_vm_intern (vm, "exists");
  ctx->s_error		= js_vm_intern (vm, "error");
  ctx->s_clearError	= js_vm_intern (vm, "clearError");

  ctx->s_autoFlush	= js_vm_intern (vm, "autoFlush");
  ctx->s_bufferSize	= js_vm_intern (vm, "bufferSize");


  /* Object information. */

  info = js_vm_builtin_info_create (vm);

  info->method_proc 		= method;
  info->property_proc 		= property;
  info->new_proc 		= new_proc;
  info->delete_proc 		= delete_proc;
  info->obj_context 		= ctx;
  info->obj_context_delete	= js_free;

  /* Define it. */
  n = &vm->globals[js_vm_intern (vm, "File")];
  js_vm_builtin_create (vm, n, info, NULL);
}


void
js_builtin_File_new (JSVirtualMachine *vm, JSNode *result_return,
		     char *path, JSIOStream *stream, int dont_close)
{
  JSNode *n;
  FileInstanceCtx *ictx;

  /* Lookup our context. */
  n = &vm->globals[js_vm_intern (vm, "File")];

  /* Create a file instance. */
  ictx = js_calloc (vm, 1, sizeof (*ictx));
  ictx->path = js_strdup (vm, path);
  ictx->stream = stream;
  ictx->dont_close = dont_close;
  ictx->vm = vm;

  /* Create the builtin. */
  js_vm_builtin_create (vm, result_return, n->u.vbuiltin->info, ictx);
}
#endif	/*CL_EXPERIMENTAL*/