/* -*- Mode: C; c-file-style: "gnu" -*-
   classpath.c -- stuff for dealing with classpaths.
`   Created: Chris Toshok <toshok@hungry.com>, 26-Jul-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 "jni.h"
#include "classpath.h"
#include "compat.h"


#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif
#ifdef HAVE_DIRENT_H
#  include <dirent.h>
#endif
#ifdef HAVE_IO_H
#  include <io.h>
#endif

#ifdef _WINDOWS
#define PATH_SEPARATOR ';'
#define PATH_SEPARATOR_STR ";"
#define DIR_SEPARATOR '\\'
#define DIR_SEPARATOR_STR "\\"
#else
#define PATH_SEPARATOR ':'
#define PATH_SEPARATOR_STR ":"
#define DIR_SEPARATOR '/'
#define DIR_SEPARATOR_STR "/"
#endif

static ClassPathEntryType
get_entry_type(char *path)
{
  struct stat buf;
  
  if (stat(path, &buf) >= 0)
    {
      if (buf.st_mode & S_IFDIR)
	return CP_DIR;
      else if (buf.st_mode & S_IFREG)
	{
	  char *period = strrchr(path, '.');
		  
	  if (!period) /* punt */
	    return CP_NONE;
	  else
	    {
	      if (!strcmp(period, ".jar")
		  || !strcmp(period, ".JAR"))
		return CP_JAR;
	      else if (!strcmp(period, ".zip")
		       || !strcmp(period, ".ZIP"))
		return CP_ZIP;
	      else
		return CP_NONE;
	    }
	}
      else
	return CP_NONE;
    }
  else
    {
      return CP_NONE;
    }
}

static int
jar_or_zip_select(struct dirent *entry)
{
  /* Linux glibc6 and Solaris is missing struct dirent.d_namlen */
  int namlen = -1;

  assert(NULL != entry);
  namlen = strlen(entry->d_name);

  if (namlen > 4)
    {
      if (!strcmp(entry->d_name + namlen - 4, ".jar")
	  || !strcmp(entry->d_name + namlen - 4, ".JAR"))
	return 1;
      else if (!strcmp(entry->d_name + namlen - 4, ".zip")
	       || !strcmp(entry->d_name + namlen - 4, ".ZIP"))
	return 1;
    }

  return 0;
}

JNIEXPORT char* JNICALL
CLASSPATH_getSystemClasspath()
{
  char *dir = CLASSZIPDIR;
  int zipdir_len = strlen(dir);
  struct dirent **namelist;
  char *sys_classpath;
  int sys_classpath_len = 0;
  int num_entries;
  int i;

  num_entries = scandir(dir, &namelist, jar_or_zip_select, alphasort);

  if (0 > num_entries) {
    fprintf(stderr, "Warning: unable to find any Java classes at %s.\n", dir);
    return strdup("");
  }

  for (i = 0; i < num_entries; i ++)
    {
      sys_classpath_len += zipdir_len + 1;
      sys_classpath_len += strlen(namelist[i]->d_name);
      if (i < num_entries - 1)
	sys_classpath_len ++;
    }

  /* +1 to make room for trailing zero when num_entries is 0 */
  sys_classpath = (char*)malloc(sys_classpath_len+1);

  sys_classpath[0] = 0;
  for (i = 0; i < num_entries; i ++)
    {
      strcat(sys_classpath, dir);
      if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
	strcat(sys_classpath, DIR_SEPARATOR_STR);
      strcat(sys_classpath, namelist[i]->d_name);
      if (i < num_entries - 1)
	strcat(sys_classpath, PATH_SEPARATOR_STR);
    }

  if (NULL != namelist)
    {
#if notyet
      for (i = 0; i < num_entries; i ++) free(namelist[i]);
      free(namelist);
#endif
    }

  return sys_classpath;
}

JNIEXPORT void JNICALL
CLASSPATH_create(const char *spec, ClasspathEntry **entries, int *num_entries)
{
  ClasspathEntry *new_cp;
  int num;
  char *class_path_entry;
  char *colon;
  int i;

  /* first we figure out the number of colons in the classpath. */
  num = 1;
  colon = (char*)spec;
  while ((colon = strchr(colon, PATH_SEPARATOR)) != NULL)
    {
      num ++;
      colon ++; /* go past the ':' */
    }

  new_cp = (ClasspathEntry*)calloc(num, sizeof(ClasspathEntry));

  class_path_entry = (char*)spec;

  i = 0;
  while (class_path_entry && class_path_entry[0] != '\0')
    {
      colon = strchr(class_path_entry, PATH_SEPARATOR);

      if (colon)
	*colon = '\0';
	  
      new_cp[i].path = strdup(class_path_entry);
      new_cp[i].type = get_entry_type(new_cp[i].path);

      if (new_cp[i].type == CP_ZIP ||
	  new_cp[i].type == CP_JAR)
	{
	  new_cp[i].zip.fd = open(new_cp[i].path, O_RDONLY
#ifdef O_BINARY
				  | O_BINARY
#endif
				  );

	  if (new_cp[i].zip.fd != -1)
	    if (read_zip_archive(&new_cp[i].zip) != 0)
	      {
		close(new_cp[i].zip.fd);
		new_cp[i].zip.fd = -1;
	      }
	}
	  
      if (colon)
	{
	  *colon = PATH_SEPARATOR;
	  class_path_entry = colon + 1;
	}
      else
	{
	  class_path_entry = NULL;
	}
      i++;
    }
  
  *entries = new_cp;
  *num_entries = num;
}

JNIEXPORT void JNICALL
CLASSPATH_destroy(ClasspathEntry *entries, int num_entries)
{
  int i;

  for (i = 0; i < num_entries; i ++)
    {
      free(entries[i].path);
      if (entries[i].type == CP_ZIP ||
          entries[i].type == CP_JAR)
        {
	  if (entries[i].zip.central_directory)
	    {
	      free(entries[i].zip.central_directory);
	      entries[i].zip.central_directory = NULL;
	    }
	  if (entries[i].zip.fd != -1)
	    {
	      close(entries[i].zip.fd);
	      entries[i].zip.fd = -1;
	    }
        }
    }

  free(entries);
}
