/* -*- Mode: C; c-file-style: "gnu" -*-
   file.c -- native methods pertaining to java.io.File
   Created: Chris Toshok <toshok@hungry.com>, 14-Aug-1997
 */
/*
  This file is part of Japhar, the GNU Virtual Machine for Java Bytecodes.
  Japhar is a project of The Hungry Programmers, GNU, and OryxSoft.

  Copyright (C) 1997, 1998, 1999 The Hungry Programmers

  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
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "jniint.h"
#include "compat.h"
#include "common.h"

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <limits.h>

#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif
#ifdef HAVE_DIRENT_H
#  include <dirent.h>
#elif defined( HAVE_DIRECT_H )
#  include <direct.h>
#endif

/* JDK1.1 */
JNIEXPORT jboolean JNICALL
Java_java_io_File_exists0(JNIEnv *env,
			  jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  struct stat buf;

  if (stat_file(henv, obj, &buf) == -1)
    return JNI_FALSE;
  else
    return JNI_TRUE;
}

/* JDK1.1 */
JNIEXPORT jboolean JNICALL
Java_java_io_File_canWrite0(JNIEnv *env,
			    jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  struct stat buf;

  if (stat_file(henv, obj, &buf) == -1)
    return JNI_FALSE;
  else
#ifdef S_IWUSR
    return (buf.st_mode & S_IWUSR) != 0;
#elif defined( _S_IWRITE)
  return (buf.st_mode & _S_IWRITE) != 0;
#endif
}

JNIEXPORT jboolean JNICALL
Java_java_io_File_canRead0(JNIEnv *env,
			   jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  struct stat buf;

  if (stat_file(henv, obj, &buf) == -1)
    return JNI_FALSE;
  else
#ifdef S_IRUSR
    return (buf.st_mode & S_IRUSR) != 0;
#elif defined( _S_IREAD )
  return (buf.st_mode & _S_IREAD) != 0;
#endif
}

JNIEXPORT jboolean JNICALL
Java_java_io_File_isFile0(JNIEnv *env,
			  jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  struct stat buf;

  if (stat_file(henv, obj, &buf) == -1)
    return JNI_FALSE;
  else
#ifdef S_ISREG
    return S_ISREG(buf.st_mode) != 0;
#elif defined _S_IFREG
  return (buf.st_mode & _S_IFREG) != 0;
#endif
}

JNIEXPORT jboolean JNICALL
Java_java_io_File_isDirectory0(JNIEnv *env,
			       jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  struct stat buf;

  if (stat_file(henv, obj, &buf) == -1)
    return JNI_FALSE;
  else
#ifdef S_ISDIR
    return S_ISDIR(buf.st_mode) != 0;
#elif defined( _S_IFDIR )
  return (buf.st_mode & _S_IFDIR) != 0;
#endif
}

JNIEXPORT jlong JNICALL
Java_java_io_File_lastModified0(JNIEnv *env,
				jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  struct stat buf;

  if (stat_file(henv, obj, &buf) == -1)
    return 0;
  else
    return (jlong)buf.st_mtime;
}

JNIEXPORT jlong JNICALL
Java_java_io_File_length0(JNIEnv *env,
			  jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  struct stat buf;

  if (stat_file(henv, obj, &buf) == -1)
    return 0;
  else
    return (jlong)buf.st_size;
}

JNIEXPORT jboolean JNICALL
Java_java_io_File_mkdir0(JNIEnv *env,
			 jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  char *path = get_file_path(henv, obj);
  jboolean result;

#ifdef MKDIR_TAKES_ONE_ARG
  /* windows defines this.  no need to check for it in configure.in */
  result = (mkdir(path) == 0);
#else
  result = (mkdir(path, 0777) == 0);
#endif

  free(path);
  return result;
}

JNIEXPORT jboolean JNICALL
Java_java_io_File_renameTo0(JNIEnv *env,
			    jobject obj,
			    jobject name)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  char *path = get_file_path(henv, obj);
  const jbyte* name_bytes;
  jboolean result;

  name_bytes = (*env)->GetStringUTFChars(env, name, NULL);

  result = (rename(path, (char*)name_bytes) == 0);

  free(path);

  (*env)->ReleaseStringUTFChars(env, name, name_bytes);

  return result;
}

JNIEXPORT jboolean JNICALL
Java_java_io_File_delete0(JNIEnv *env,
			  jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  char *path = get_file_path(henv, obj);
  jboolean result;

  result = (unlink(path) == 0);

  free(path);

  return result;
}

JNIEXPORT jboolean JNICALL
Java_java_io_File_rmdir0(JNIEnv *env,
			 jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  char *path = get_file_path(henv, obj);
  jboolean result;

  result = (rmdir(path) == 0);

  free(path);

  return result;
}

static int
scandir_select(struct dirent *entry)
{
  if (!strcmp(entry->d_name, ".")
      || !strcmp(entry->d_name, ".."))
    return 0;
  else
    return 1;
}

JNIEXPORT jobjectArray JNICALL
Java_java_io_File_list0(JNIEnv *env,
			jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  char *path = get_file_path(henv, obj);
  struct dirent **entries;
  int num_entries;
  jobjectArray elements;
  jclass string_class = (*env)->FindClass(env, "java/lang/String");
  int i;

  num_entries = scandir(path, &entries, scandir_select, NULL);

  if (num_entries == -1)
    {
      HVM_ExceptionThrow(henv, "java/lang/OutOfMemory", "In java.io.File.list0");
      return NULL;
    }

  elements = (*env)->NewObjectArray(env, num_entries, string_class,
				    NULL);

  for (i = 0; i < num_entries; i ++)
    {
      jobject element = (*env)->NewStringUTF(env, entries[i]->d_name);

      (*env)->SetObjectArrayElement(env, elements, i, element);
    }

  free(entries);

  return elements;
}

/* JDK1.1 */
JNIEXPORT jstring JNICALL
Java_java_io_File_canonPath(JNIEnv *env,
			    jobject obj,
			    jobject path)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  char *path_str = HVM_StringToCString(henv, path);
  jstring ret_val;

  char resulting_path[ PATH_MAX + 1 ];
  int len;

  len = resolvepath(path_str, resulting_path, sizeof(resulting_path));

  resulting_path[ len ] = 0;

  ret_val = (*env)->NewStringUTF(env, resulting_path);

  free(path_str);

  return path;
}

JNIEXPORT jboolean JNICALL
Java_java_io_File_isAbsolute(JNIEnv *env,
			     jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jclass clazz;
  jfieldID separatorChar_id;
  jchar separator;
  jboolean absolute = JNI_FALSE;
  char *path;

  assert(NULL != env);
  assert(NULL != obj);

  path = get_file_path(henv, obj);
  assert(NULL != path);

  clazz = (*env)->GetObjectClass(env, obj);
  assert(NULL != clazz);

  separatorChar_id = (*env)->GetStaticFieldID(env, clazz, "separatorChar", "C");
  assert(NULL != separatorChar_id);

  separator = (*env)->GetStaticCharField(env, clazz, separatorChar_id);
  /* XXX This assert triggers on SPARC/Solaris [pere 1998-05-08] */
  assert(0 != separator);

  absolute = path[0] == (separator & 0xff); /* XXX only handle ISO 8859/1 */

  /*
   * Handle MSDOS C:\full\path
   */
  if ( ! absolute && ('\\' == (separator & 0xff)) &&
       2 >= strlen(path) && ':' == path[1] && isalpha(path[0]) )
    absolute = JNI_TRUE;

  free(path);
  return absolute;
}

