/* -*- Mode: C; c-file-style: "gnu" -*-
   japharh.c -- output headers and stubs for java class files.
   Created: Chris Toshok <toshok@hungry.com>
 */
/*
  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) 1998 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 "resolve.h"
#include "log.h"
#include "japharh.h"
#include "util.h"
#include "objects.h"

#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <assert.h>

#define MYLOG "Javah"

typedef enum {
  HEADER,
  STUBS,
  DUMP
} OutputType;

char *javah_dest_dir = NULL;
OutputType output_type = HEADER;
static int initial_class_pos = 1;
char *classpath = NULL;
jboolean as_html = JNI_FALSE;

static JavaVMOption options[1024]; /* hope noone ever needs more than 1024 command line options :) */
static int num_options = 0;

JNIEnv *the_env = NULL;

static void
usage(char *name)
{
  printf ("Usage: %s [OPTION]... CLASS [CLASSES]...\n", name);
  printf ("output headers or stubs for each class listed on the command line\n"
	  "  -d=PATH          sets the destination directory to PATH.\n"
	  "  -classpath=PATH  use PATH to lookup classes.\n"
          "  -help            display this help and exit.\n"
          "  -version         output version information and exit.\n"
	  "  -stubs           output stubs for the classes listed.\n"
	  "  -header          output headers for the classes listed (default).\n"
	  "  -dump            output dump format (formally dump-class).\n"
	  "    -ascii         output the files as plain ascii text. (default) - only available when -dump\n"
	  "    -html          output the files using HTML.  only available when -dump\n");
}

static void
parse_command_line(int argc, char **argv)
{
  int i;
  char *classpath = NULL;

  options[num_options++].optionString = "-Djava.library.path=/usr/local/japhar/lib" /* XXX */;
  options[num_options++].optionString = "-Dsun.boot.library.path=/usr/local/japhar/lib" /* XXX */;
  options[num_options++].optionString = "-Djava.awt.graphicsenv=sun/awt/X11GraphicsEnvironment" /* XXX */;

  for (i = 1; i < argc; i ++)
    {
      char *arg;
      jboolean is_switch = JNI_FALSE;
      int two_dashes = 0;

      arg = argv[i];

      /* handle both -arg and --arg */
      if (arg[0] == '-') { arg++; is_switch = JNI_TRUE; }
      if (arg[0] == '-') { arg++; two_dashes = 1; }
      
      if (!strcmp(arg, "version"))
	{
          printf ("GNU Japharh %s\n"
                  "Copyright (C) 1998 The Hungry Programmers\n"
                  "Japhar comes with ABSOLUTELY NO WARRANTY.\n"
                  "You may redistribute copies of japharh\n"
                  "under the terms of the GNU General Public License.\n"
                  "For more information about these matters, see the "
                  "files named COPYING.\n", VERSION);
	  exit(0);
	}
      else if (!strcmp(arg, "help"))
	{
	  usage(argv[0]);
	  exit(0);
	}
      else if (!strncmp(arg, "classpath=", 10 /*strlen(classpath=)*/)) 
	{
	  classpath = arg + 10 /* strlen(classpath=) */;
	}
      else if (!strncmp(arg, "verbose:", 8 /* strlen(verbose:) */))
	{
	  options[num_options++].optionString = argv[i] + two_dashes;
	}
      else if (!strncmp(arg, "stubs", 5 /* strlen(stubs) */))
	{
	  output_type = STUBS;
	}
      else if (!strncmp(arg, "dump", 4 /* strlen(dump) */))
	{
	  output_type = DUMP;
	}
      else if (!strncmp(arg, "html", 4 /* strlen(html) */))
	{
	  as_html = JNI_TRUE;
	}
      else if (!strncmp(arg, "ascii", 5 /* strlen(ascii) */))
	{
	  as_html = JNI_FALSE;
	}
      else if (arg[0] =='d')
	{
	  javah_dest_dir = argv[++i];
	}
      else if (arg[0] == 'D' || arg[0] == 'X')
	{
	  options[num_options++].optionString = argv[i] + two_dashes;
	}
      else
	{
	  if (is_switch)
	    {
	      fprintf (stderr, "unrecognized option: %s\n", argv[i]);
	      usage(argv[0]);
	      exit(0);
	    }
	  else
	    {
	      initial_class_pos = i;
	      break;
	    }
	}
    }

  {
    char *cp_value;
    char *new_cp_option;

    if (classpath)
      cp_value = classpath;
    else
      {
	char *classpath_env = getenv("CLASSPATH");
	
	if (classpath_env)
	  cp_value = classpath_env;
	else
	  cp_value = CLASSPATH_getSystemClasspath();
      }
    
    new_cp_option = malloc(strlen("-Djava.class.path=") + strlen(cp_value) + 1);
    strcpy(new_cp_option, "-Djava.class.path=");
    strcat(new_cp_option, cp_value);
    
    options[num_options++].optionString = new_cp_option;
   
  } 

  if (initial_class_pos == -1)
    { 
      usage (argv[0]);
      exit (0);
    }
}

static void
check_dest_dir(char *dest_dir)
{
  struct stat buf;
  int stat_result;

  stat_result = stat(dest_dir, &buf);

  if (stat_result != -1
      && !S_ISDIR(buf.st_mode))
    {
      fprintf (stderr, "Destination directory %s is not a directory\n", dest_dir);
      exit(1);
    }
  else if (stat_result == -1)
    {
      if (mkdir(dest_dir, 0777) == -1)
	{
	  fprintf (stderr, "Could not create the destination directory %s\n", dest_dir);
	  exit(1);
	}
    }
}

int main (int argc, char *argv[])
{
  JavaVM *jvm;
  JavaVMInitArgs vm_args;
  int i;
  jclass cls;

  parse_command_line(argc, argv);

  if (javah_dest_dir == NULL)
    javah_dest_dir = "./";

  check_dest_dir(javah_dest_dir);

  /*
  ** Get the default initialization arguments and set the class path.
  */
  vm_args.version = JNI_VERSION_1_2;
  JNI_GetDefaultJavaVMInitArgs(&vm_args);

  options[num_options++].optionString = "-Djava.library.path=/usr/local/japhar/lib" /* XXX */;
  options[num_options++].optionString = "-Dsun.boot.library.path=/usr/local/japhar/lib" /* XXX */;
  options[num_options++].optionString = "-Djava.awt.graphicsenv=sun/awt/X11GraphicsEnvironment" /* XXX */;

  {
    char *classpath = NULL; /* XXX */
    char *cp_value;
    char *new_cp_option;

    if (classpath)
      cp_value = classpath;
    else
      {
	char *classpath_env = getenv("CLASSPATH");
	
	if (classpath_env)
	  cp_value = classpath_env;
	else
	  cp_value = CLASSPATH_getSystemClasspath();
      }
    
    new_cp_option = malloc(strlen("-Djava.class.path=") + strlen(cp_value) + 1);
    strcpy(new_cp_option, "-Djava.class.path=");
    strcat(new_cp_option, cp_value);
    
    options[num_options++].optionString = new_cp_option;
  }

  vm_args.options = options;
  vm_args.nOptions = num_options;
  vm_args.ignoreUnrecognized = JNI_FALSE;

  /*
  ** load and initialize a JavaVM, return a JNI interface pointer in
  ** env.
  */
  JNI_CreateJavaVM(&jvm, &the_env, &vm_args);

  for (i = initial_class_pos; i < argc; i ++)
    {
      char *class_name = argv[i];

      JAVARLOG1(MYLOG, 1, "  Processing class '%s'...\n", class_name);

      cls = (*the_env)->FindClass(the_env, class_name);

      if (cls == NULL)
	{
	  fprintf (stderr, "Unable to load class %s\n", class_name);
	  exit(1);
	}

      switch (output_type)
	{
	case HEADER:
	  spew_header(jclass_to_clazzfile(the_env, cls), class_name);
	  break;
	case STUBS:
	  spew_stubs(jclass_to_clazzfile(the_env, cls), class_name);
	  break;
	case DUMP:
	  spew_dump(jclass_to_clazzfile(the_env, cls), class_name);
	  break;
	}
    }

  return 0;
}
