#include "config.h"
#include "system.h"
#include <math.h>
#include "jcf.h"
#include "tree.h"
#include "javaop.h"
#include "java-tree.h"
#include "java-opcodes.h"
#include "ggc.h"
#include "hashtab.h"
#include <getopt.h>
FILE *out = NULL;
static int found_error = 0;
static int flag_jni = 0;
int flag_newer = 1;
const char *output_directory = "";
const char *temp_directory = "/tmp";
static int friend_count;
static char **friend_specs = NULL;
static int prepend_count;
static char **prepend_specs = NULL;
static int add_count;
static char **add_specs = NULL;
static int append_count;
static char **append_specs = NULL;
int verbose = 0;
int stubs = 0;
struct JCF *current_jcf;
static JCF_u2 last_access;
#define METHOD_IS_FINAL(Class, Method) \
(((Class) & ACC_FINAL) || ((Method) & (ACC_FINAL | ACC_PRIVATE)))
#define METHOD_IS_NATIVE(Method) \
((Method) & ACC_NATIVE)
struct method_name
{
unsigned char *name;
int length;
unsigned char *signature;
int sig_length;
struct method_name *next;
};
static struct method_name *method_name_list;
static void print_field_info PARAMS ((FILE*, JCF*, int, int, JCF_u2));
static void print_mangled_classname PARAMS ((FILE*, JCF*, const char*, int));
static int print_cxx_classname PARAMS ((FILE*, const char*, JCF*, int));
static void print_method_info PARAMS ((FILE*, JCF*, int, int, JCF_u2));
static void print_c_decl PARAMS ((FILE*, JCF*, int, int, int, const char *,
int));
static void print_stub_or_jni PARAMS ((FILE*, JCF*, int, int, int,
const char *, int));
static void print_full_cxx_name PARAMS ((FILE*, JCF*, int, int, int,
const char *, int));
static void decompile_method PARAMS ((FILE*, JCF*, int));
static void add_class_decl PARAMS ((FILE*, JCF*, JCF_u2));
static int java_float_finite PARAMS ((jfloat));
static int java_double_finite PARAMS ((jdouble));
static void print_name PARAMS ((FILE *, JCF *, int));
static void print_base_classname PARAMS ((FILE *, JCF *, int));
static int utf8_cmp PARAMS ((const unsigned char *, int, const char *));
static char *cxx_keyword_subst PARAMS ((const unsigned char *, int));
static void generate_access PARAMS ((FILE *, JCF_u2));
static int name_is_method_p PARAMS ((const unsigned char *, int));
static char *get_field_name PARAMS ((JCF *, int, JCF_u2));
static void print_field_name PARAMS ((FILE *, JCF *, int, JCF_u2));
static const unsigned char *super_class_name PARAMS ((JCF *, int *));
static void print_include PARAMS ((FILE *, const unsigned char *, int));
static int gcjh_streq PARAMS ((const void *p1, const void *p2));
static int throwable_p PARAMS ((const unsigned char *signature));
static const unsigned char *decode_signature_piece
PARAMS ((FILE *, const unsigned char *, const unsigned char *, int *));
static void print_class_decls PARAMS ((FILE *, JCF *, int));
static void usage PARAMS ((void)) ATTRIBUTE_NORETURN;
static void help PARAMS ((void)) ATTRIBUTE_NORETURN;
static void version PARAMS ((void)) ATTRIBUTE_NORETURN;
static int overloaded_jni_method_exists_p PARAMS ((const unsigned char *, int,
const char *, int));
static void jni_print_char PARAMS ((FILE *, int));
static void decompile_return_statement PARAMS ((FILE *, JCF *, int, int, int));
JCF_u2 current_field_name;
JCF_u2 current_field_value;
JCF_u2 current_field_signature;
JCF_u2 current_field_flags;
#define HANDLE_START_FIELD(ACCESS_FLAGS, NAME, SIGNATURE, ATTRIBUTE_COUNT) \
( current_field_name = (NAME), current_field_signature = (SIGNATURE), \
current_field_flags = (ACCESS_FLAGS), current_field_value = 0)
static int field_pass;
static int method_pass;
#define HANDLE_END_FIELD() \
if (field_pass) \
{ \
if (out && ! stubs && ! flag_jni) \
print_field_info (out, jcf, current_field_name, \
current_field_signature, \
current_field_flags); \
} \
else if (! stubs && ! flag_jni) \
add_class_decl (out, jcf, current_field_signature);
#define HANDLE_CONSTANTVALUE(VALUEINDEX) current_field_value = (VALUEINDEX)
static int method_declared = 0;
static int method_access = 0;
static int method_printed = 0;
static int method_synthetic = 0;
static int method_signature = 0;
#define HANDLE_METHOD(ACCESS_FLAGS, NAME, SIGNATURE, ATTRIBUTE_COUNT) \
{ \
method_synthetic = 0; \
method_printed = 0; \
decompiled = 0; \
method_signature = SIGNATURE; \
if (ATTRIBUTE_COUNT) \
method_synthetic = peek_attribute (jcf, ATTRIBUTE_COUNT, \
(const char *)"Synthetic", 9); \
\
if (method_synthetic) \
{ \
skip_attribute (jcf, ATTRIBUTE_COUNT); \
ATTRIBUTE_COUNT = 0; \
} \
if (method_pass && !method_synthetic) \
{ \
if (out) \
print_method_info (out, jcf, NAME, SIGNATURE, \
ACCESS_FLAGS); \
} \
else if (!method_synthetic) \
{ \
print_method_info (NULL, jcf, NAME, SIGNATURE, \
ACCESS_FLAGS); \
if (! stubs && ! flag_jni) \
add_class_decl (out, jcf, SIGNATURE); \
} \
}
#define HANDLE_CODE_ATTRIBUTE(MAX_STACK, MAX_LOCALS, CODE_LENGTH) \
if (out && method_declared) decompile_method (out, jcf, CODE_LENGTH);
static int decompiled = 0;
#define HANDLE_END_METHOD() \
if (out && method_printed && !method_synthetic) \
fputs (decompiled || stubs ? "\n" : ";\n", out);
#define NEED_PEEK_ATTRIBUTE
#define NEED_SKIP_ATTRIBUTE
#include "jcf-reader.c"
#define F_NAN_MASK 0x7f800000
#if (1 == HOST_FLOAT_WORDS_BIG_ENDIAN) && ! defined (HOST_WORDS_BIG_ENDIAN)
#define D_NAN_MASK 0x000000007ff00000LL
#else
#define D_NAN_MASK 0x7ff0000000000000LL
#endif
static int
java_float_finite (f)
jfloat f;
{
union Word u;
u.f = f;
return (u.i & F_NAN_MASK) != F_NAN_MASK;
}
static int
java_double_finite (d)
jdouble d;
{
union DWord u;
u.d = d;
return (u.l & D_NAN_MASK) != D_NAN_MASK;
}
static void
jni_print_char (stream, ch)
FILE *stream;
int ch;
{
if (! flag_jni)
jcf_print_char (stream, ch);
else if (ch == '(' || ch == ')')
{
}
else if (ch == '_')
fputs ("_1", stream);
else if (ch == ';')
fputs ("_2", stream);
else if (ch == '[')
fputs ("_3", stream);
else if (ch == '/')
fputs ("_", stream);
else if (ISALNUM (ch))
fputc (ch, stream);
else
{
fprintf (stream, "_0%04x", ch);
}
}
static void
DEFUN(print_name, (stream, jcf, name_index),
FILE* stream AND JCF* jcf AND int name_index)
{
if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
{
fprintf (stream, "<not a UTF8 constant>");
found_error = 1;
}
else if (! flag_jni)
jcf_print_utf8 (stream, JPOOL_UTF_DATA (jcf, name_index),
JPOOL_UTF_LENGTH (jcf, name_index));
else
{
const unsigned char *str = JPOOL_UTF_DATA (jcf, name_index);
int length = JPOOL_UTF_LENGTH (jcf, name_index);
const unsigned char *limit = str + length;
while (str < limit)
{
int ch = UTF8_GET (str, limit);
if (ch < 0)
{
fprintf (stream, "\\<invalid>");
return;
}
jni_print_char (stream, ch);
}
}
}
static void
print_base_classname (stream, jcf, index)
FILE *stream;
JCF *jcf;
int index;
{
int name_index = JPOOL_USHORT1 (jcf, index);
int len;
const unsigned char *s, *p, *limit;
s = JPOOL_UTF_DATA (jcf, name_index);
len = JPOOL_UTF_LENGTH (jcf, name_index);
limit = s + len;
p = s;
while (s < limit)
{
int c = UTF8_GET (s, limit);
if (c == '/')
p = s;
}
while (p < limit)
{
int ch = UTF8_GET (p, limit);
if (ch == '/')
fputs ("::", stream);
else
jcf_print_char (stream, ch);
}
}
static int
utf8_cmp (str, length, name)
const unsigned char *str;
int length;
const char *name;
{
const unsigned char *limit = str + length;
int i;
for (i = 0; name[i]; ++i)
{
int ch = UTF8_GET (str, limit);
if (ch != name[i])
return ch - name[i];
}
return str == limit ? 0 : 1;
}
static const char *const cxx_keywords[] =
{
"_Complex",
"__alignof",
"__alignof__",
"__asm",
"__asm__",
"__attribute",
"__attribute__",
"__builtin_va_arg",
"__complex",
"__complex__",
"__const",
"__const__",
"__extension__",
"__imag",
"__imag__",
"__inline",
"__inline__",
"__label__",
"__null",
"__real",
"__real__",
"__restrict",
"__restrict__",
"__signed",
"__signed__",
"__typeof",
"__typeof__",
"__volatile",
"__volatile__",
"and",
"and_eq",
"asm",
"auto",
"bitand",
"bitor",
"bool",
"break",
"case",
"catch",
"char",
"class",
"compl",
"const",
"const_cast",
"continue",
"default",
"delete",
"do",
"double",
"dynamic_cast",
"else",
"enum",
"explicit",
"export",
"extern",
"false",
"float",
"for",
"friend",
"goto",
"if",
"inline",
"int",
"long",
"mutable",
"namespace",
"new",
"not",
"not_eq",
"operator",
"or",
"or_eq",
"private",
"protected",
"public",
"register",
"reinterpret_cast",
"return",
"short",
"signed",
"sizeof",
"static",
"static_cast",
"struct",
"switch",
"template",
"this",
"throw",
"true",
"try",
"typedef",
"typeid",
"typename",
"typeof",
"union",
"unsigned",
"using",
"virtual",
"void",
"volatile",
"wchar_t",
"while",
"xor",
"xor_eq"
};
static char *
cxx_keyword_subst (str, length)
const unsigned char *str;
int length;
{
int last = ARRAY_SIZE (cxx_keywords);
int first = 0;
int mid = (last + first) / 2;
int old = -1;
for (mid = (last + first) / 2;
mid != old;
old = mid, mid = (last + first) / 2)
{
int kwl = strlen (cxx_keywords[mid]);
int min_length = kwl > length ? length : kwl;
int r = utf8_cmp (str, min_length, cxx_keywords[mid]);
if (r == 0)
{
int i;
for (i = min_length; i < length && str[i] == '$'; ++i)
;
if (i == length)
{
char *dup = xmalloc (2 + length - min_length + kwl);
strcpy (dup, cxx_keywords[mid]);
for (i = kwl; i < length + 1; ++i)
dup[i] = '$';
dup[i] = '\0';
return dup;
}
r = 1;
}
if (r < 0)
last = mid;
else
first = mid;
}
return NULL;
}
static void
generate_access (stream, flags)
FILE *stream;
JCF_u2 flags;
{
if ((flags & ACC_VISIBILITY) == last_access)
return;
last_access = (flags & ACC_VISIBILITY);
switch (last_access)
{
case 0:
fputs ("public: // actually package-private\n", stream);
break;
case ACC_PUBLIC:
fputs ("public:\n", stream);
break;
case ACC_PRIVATE:
fputs ("private:\n", stream);
break;
case ACC_PROTECTED:
fputs ("public: // actually protected\n", stream);
break;
default:
found_error = 1;
fprintf (stream, "#error unrecognized visibility %d\n",
(flags & ACC_VISIBILITY));
break;
}
}
static int
name_is_method_p (name, length)
const unsigned char *name;
int length;
{
struct method_name *p;
for (p = method_name_list; p != NULL; p = p->next)
{
if (p->length == length && ! memcmp (p->name, name, length))
return 1;
}
return 0;
}
static int
overloaded_jni_method_exists_p (name, length, signature, sig_length)
const unsigned char *name;
int length;
const char *signature;
int sig_length;
{
struct method_name *p;
for (p = method_name_list; p != NULL; p = p->next)
{
if (p->length == length
&& ! memcmp (p->name, name, length)
&& (p->sig_length != sig_length
|| memcmp (p->signature, signature, sig_length)))
return 1;
}
return 0;
}
static char *
get_field_name (jcf, name_index, flags)
JCF *jcf;
int name_index;
JCF_u2 flags;
{
unsigned char *name = JPOOL_UTF_DATA (jcf, name_index);
int length = JPOOL_UTF_LENGTH (jcf, name_index);
char *override;
if (name_is_method_p (name, length))
{
if ((flags & ACC_STATIC))
{
fprintf (stderr, "static field has same name as method\n");
found_error = 1;
return NULL;
}
override = xmalloc (length + 3);
memcpy (override, name, length);
strcpy (override + length, "__");
}
else
override = cxx_keyword_subst (name, length);
return override;
}
static void
print_field_name (stream, jcf, name_index, flags)
FILE *stream;
JCF *jcf;
int name_index;
JCF_u2 flags;
{
char *override = get_field_name (jcf, name_index, flags);
if (override)
{
fputs (override, stream);
free (override);
}
else
jcf_print_utf8 (stream, JPOOL_UTF_DATA (jcf, name_index),
JPOOL_UTF_LENGTH (jcf, name_index));
}
static void
DEFUN(print_field_info, (stream, jcf, name_index, sig_index, flags),
FILE *stream AND JCF* jcf
AND int name_index AND int sig_index AND JCF_u2 flags)
{
char *override = NULL;
generate_access (stream, flags);
if (JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
{
fprintf (stream, "<not a UTF8 constant>");
found_error = 1;
return;
}
fputs (" ", out);
if ((flags & ACC_STATIC))
fputs ("static ", out);
if ((flags & ACC_FINAL))
{
if (current_field_value > 0)
{
char buffer[25];
int done = 1;
switch (JPOOL_TAG (jcf, current_field_value))
{
case CONSTANT_Integer:
{
jint num;
int most_negative = 0;
fputs ("const jint ", out);
print_field_name (out, jcf, name_index, 0);
fputs (" = ", out);
num = JPOOL_INT (jcf, current_field_value);
if (num == (jint) 0x80000000)
{
most_negative = 1;
++num;
}
format_int (buffer, (jlong) num, 10);
fprintf (out, "%sL%s;\n", buffer, most_negative ? " - 1" : "");
}
break;
case CONSTANT_Long:
{
jlong num;
int most_negative = 0;
fputs ("const jlong ", out);
print_field_name (out, jcf, name_index, 0);
fputs (" = ", out);
num = JPOOL_LONG (jcf, current_field_value);
if (num == (jlong) 0x8000000000000000LL)
{
most_negative = 1;
++num;
}
format_int (buffer, num, 10);
fprintf (out, "%sLL%s;\n", buffer, most_negative ? " - 1" :"");
}
break;
case CONSTANT_Float:
{
jfloat fnum = JPOOL_FLOAT (jcf, current_field_value);
fputs ("const jfloat ", out);
print_field_name (out, jcf, name_index, 0);
if (! java_float_finite (fnum))
fputs (";\n", out);
else
fprintf (out, " = %.10g;\n", fnum);
}
break;
case CONSTANT_Double:
{
jdouble dnum = JPOOL_DOUBLE (jcf, current_field_value);
fputs ("const jdouble ", out);
print_field_name (out, jcf, name_index, 0);
if (! java_double_finite (dnum))
fputs (";\n", out);
else
fprintf (out, " = %.17g;\n", dnum);
}
break;
default:
done = 0;
break;
}
if (done)
return;
}
}
override = get_field_name (jcf, name_index, flags);
print_c_decl (out, jcf, name_index, sig_index, 0, override, flags);
fputs (";\n", out);
if (override)
free (override);
}
static void
DEFUN(print_method_info, (stream, jcf, name_index, sig_index, flags),
FILE *stream AND JCF* jcf
AND int name_index AND int sig_index AND JCF_u2 flags)
{
const unsigned char *str;
int length, is_init = 0;
char *override = NULL;
method_declared = 0;
method_access = flags;
if (stream && JPOOL_TAG (jcf, name_index) != CONSTANT_Utf8)
fprintf (stream, "<not a UTF8 constant>");
str = JPOOL_UTF_DATA (jcf, name_index);
length = JPOOL_UTF_LENGTH (jcf, name_index);
if (str[0] == '<')
{
if (! utf8_cmp (str, length, "<init>"))
is_init = 1;
else if (! METHOD_IS_FINAL (jcf->access_flags, flags)
&& ! (flags & ACC_STATIC))
{
fprintf (stderr, "ignored method `");
jcf_print_utf8 (stderr, str, length);
fprintf (stderr, "' marked virtual\n");
found_error = 1;
return;
}
else
return;
}
if (! stream)
{
struct method_name *nn;
nn = xmalloc (sizeof (struct method_name));
nn->name = xmalloc (length);
memcpy (nn->name, str, length);
nn->length = length;
nn->next = method_name_list;
nn->sig_length = JPOOL_UTF_LENGTH (jcf, sig_index);
nn->signature = xmalloc (nn->sig_length);
memcpy (nn->signature, JPOOL_UTF_DATA (jcf, sig_index),
nn->sig_length);
method_name_list = nn;
return;
}
if (! flag_jni)
{
override = cxx_keyword_subst (str, length);
}
if (! stubs && ! flag_jni)
{
method_printed = 1;
generate_access (stream, flags);
fputs (" ", out);
if ((flags & ACC_STATIC))
fputs ("static ", out);
else if (! METHOD_IS_FINAL (jcf->access_flags, flags))
{
if (! is_init)
fputs ("virtual ", out);
}
print_c_decl (out, jcf, name_index, sig_index, is_init, override, flags);
if ((flags & ACC_ABSTRACT))
fputs (" = 0", out);
else
method_declared = 1;
}
else
{
if (METHOD_IS_NATIVE (flags))
{
method_printed = 1;
print_stub_or_jni (out, jcf, name_index, sig_index,
is_init, override, flags);
}
}
if (override)
free (override);
}
static void
decompile_return_statement (out, jcf, methodtype, nameindex, objecttype)
FILE *out;
JCF *jcf;
int methodtype, nameindex, objecttype;
{
int cast = 0;
int obj_name_len, method_name_len;
const unsigned char *obj_data, *method_data;
obj_name_len = JPOOL_UTF_LENGTH (jcf, objecttype);
obj_data = JPOOL_UTF_DATA (jcf, objecttype);
method_name_len = JPOOL_UTF_LENGTH (jcf, methodtype);
method_data = JPOOL_UTF_DATA (jcf, methodtype);
while (*method_data != ')')
{
++method_data;
--method_name_len;
}
++method_data;
--method_name_len;
if (method_data[0] == 'L' && method_data[method_name_len - 1] == ';')
{
++method_data;
method_name_len -= 2;
}
if (obj_data[0] == 'L' && obj_data[obj_name_len - 1] == ';')
{
++obj_data;
obj_name_len -= 2;
}
if (method_name_len != obj_name_len)
cast = 1;
else
{
int i;
for (i = 0; i < method_name_len; ++i)
{
if (method_data[i] != obj_data[i])
{
cast = 1;
break;
}
}
}
fputs (" { return ", out);
if (cast)
{
int array_depth = 0;
const unsigned char *limit;
fputs ("reinterpret_cast<", out);
while (*method_data == '[')
{
++method_data;
++array_depth;
--method_name_len;
fputs ("JArray<", out);
}
fputs (" ::", out);
if (array_depth && method_data[0] == 'L'
&& method_data[method_name_len - 1] == ';')
{
++method_data;
method_name_len -= 2;
}
limit = method_data + method_name_len;
while (method_data < limit)
{
int ch = UTF8_GET (method_data, limit);
if (ch == '/')
fputs ("::", out);
else
jcf_print_char (out, ch);
}
fputs (" *", out);
while (array_depth > 0)
{
fputs ("> *", out);
--array_depth;
}
fputs ("> (", out);
}
if (nameindex == -1)
fputs ("this", out);
else
print_field_name (out, jcf, nameindex, 0);
if (cast)
fputs (")", out);
fputs ("; }", out);
}
static void
decompile_method (out, jcf, code_len)
FILE *out;
JCF *jcf;
int code_len;
{
const unsigned char *codes = jcf->read_ptr;
int index;
uint16 name_and_type, name;
if ((method_access & ACC_SYNCHRONIZED))
return;
if (code_len == 5
&& codes[0] == OPCODE_aload_0
&& codes[1] == OPCODE_getfield
&& (codes[4] == OPCODE_areturn
|| codes[4] == OPCODE_dreturn
|| codes[4] == OPCODE_freturn
|| codes[4] == OPCODE_ireturn
|| codes[4] == OPCODE_lreturn))
{
index = (codes[2] << 8) | codes[3];
name_and_type = JPOOL_USHORT2 (jcf, index);
name = JPOOL_USHORT1 (jcf, name_and_type);
if (codes[4] == OPCODE_areturn)
decompile_return_statement (out, jcf, method_signature,
name, JPOOL_USHORT2 (jcf, name_and_type));
else
{
fputs (" { return ", out);
print_field_name (out, jcf, name, 0);
fputs ("; }", out);
}
decompiled = 1;
}
else if (code_len == 2
&& codes[0] == OPCODE_aload_0
&& codes[1] == OPCODE_areturn
&& ! (method_access & ACC_STATIC))
{
decompile_return_statement (out, jcf, method_signature, -1,
JPOOL_USHORT1 (jcf, jcf->this_class));
decompiled = 1;
}
else if (code_len == 1 && codes[0] == OPCODE_return)
{
fputs (" { }", out);
decompiled = 1;
}
else if (code_len == 2
&& codes[0] == OPCODE_aconst_null
&& codes[1] == OPCODE_areturn)
{
fputs (" { return 0; }", out);
decompiled = 1;
}
}
static int
gcjh_streq (p1, p2)
const void *p1, *p2;
{
return ! strcmp ((char *) p1, (char *) p2);
}
static int
throwable_p (clname)
const unsigned char *clname;
{
int length;
unsigned char *current;
int i;
int result = 0;
static htab_t throw_hash;
static htab_t non_throw_hash;
static int init_done = 0;
if (! init_done)
{
PTR *slot;
const unsigned char *str;
throw_hash = htab_create (10, htab_hash_string, gcjh_streq,
(htab_del) free);
non_throw_hash = htab_create (10, htab_hash_string, gcjh_streq,
(htab_del) free);
str = xstrdup ("java.lang.Throwable");
slot = htab_find_slot (throw_hash, str, INSERT);
*slot = (PTR) str;
str = xstrdup ("java.lang.Object");
slot = htab_find_slot (non_throw_hash, str, INSERT);
*slot = (PTR) str;
init_done = 1;
}
for (length = 0; clname[length] != ';' && clname[length] != '\0'; ++length)
;
current = ALLOC (length + 1);
for (i = 0; i < length; ++i)
current[i] = clname[i] == '/' ? '.' : clname[i];
current[length] = '\0';
if (htab_find (throw_hash, current))
result = 1;
else if (htab_find (non_throw_hash, current))
result = 0;
else
{
JCF jcf;
PTR *slot;
unsigned char *super, *tmp;
int super_length = -1;
const char *classfile_name = find_class (current, strlen (current),
&jcf, 0);
if (! classfile_name)
{
fprintf (stderr, "couldn't find class %s\n", current);
found_error = 1;
return 0;
}
if (jcf_parse_preamble (&jcf) != 0
|| jcf_parse_constant_pool (&jcf) != 0
|| verify_constant_pool (&jcf) > 0)
{
fprintf (stderr, "parse error while reading %s\n", classfile_name);
found_error = 1;
return 0;
}
jcf_parse_class (&jcf);
tmp = (unsigned char *) super_class_name (&jcf, &super_length);
super = ALLOC (super_length + 1);
memcpy (super, tmp, super_length);
super[super_length] = '\0';
result = throwable_p (super);
slot = htab_find_slot (result ? throw_hash : non_throw_hash,
current, INSERT);
*slot = current;
current = NULL;
JCF_FINISH (&jcf);
}
return result;
}
static const unsigned char *
decode_signature_piece (stream, signature, limit, need_space)
FILE *stream;
const unsigned char *signature, *limit;
int *need_space;
{
const char *ctype;
int array_depth = 0;
switch (signature[0])
{
case '[':
array_loop:
for (signature++; (signature < limit
&& ISDIGIT (*signature)); signature++)
;
switch (*signature)
{
case 'B':
ctype = "jbyteArray";
break;
case 'C':
ctype = "jcharArray";
break;
case 'D':
ctype = "jdoubleArray";
break;
case 'F':
ctype = "jfloatArray";
break;
case 'I':
ctype = "jintArray";
break;
case 'S':
ctype = "jshortArray";
break;
case 'J':
ctype = "jlongArray";
break;
case 'Z':
ctype = "jbooleanArray";
break;
case '[':
++array_depth;
if (! flag_jni)
fputs ("JArray<", stream);
goto array_loop;
case 'L':
++signature;
if (! flag_jni)
fputs ("JArray< ::", stream);
while (signature < limit && *signature != ';')
{
int ch = UTF8_GET (signature, limit);
if (! flag_jni)
{
if (ch == '/')
fputs ("::", stream);
else
jcf_print_char (stream, ch);
}
}
if (! flag_jni)
fputs (" *> *", stream);
*need_space = 0;
ctype = NULL;
break;
default:
return NULL;
}
if (flag_jni && (ctype == NULL || array_depth > 0))
{
ctype = "jobjectArray";
*need_space = 1;
}
if (ctype != NULL)
goto printit;
++signature;
break;
case '(':
case ')':
return NULL;
case 'B': ctype = "jbyte"; goto printit;
case 'C': ctype = "jchar"; goto printit;
case 'D': ctype = "jdouble"; goto printit;
case 'F': ctype = "jfloat"; goto printit;
case 'I': ctype = "jint"; goto printit;
case 'J': ctype = "jlong"; goto printit;
case 'S': ctype = "jshort"; goto printit;
case 'Z': ctype = "jboolean"; goto printit;
case 'V': ctype = "void"; goto printit;
case 'L':
if (flag_jni)
{
if (! strncmp (signature, "Ljava/lang/String;",
sizeof ("Ljava/lang/String;") -1))
ctype = "jstring";
else if (! strncmp (signature, "Ljava/lang/Class;",
sizeof ("Ljava/lang/Class;") - 1))
ctype = "jclass";
else if (throwable_p (signature + 1))
ctype = "jthrowable";
else
ctype = "jobject";
while (*signature && *signature != ';')
++signature;
goto printit;
}
fputs ("::", stream);
++signature;
while (*signature && *signature != ';')
{
int ch = UTF8_GET (signature, limit);
if (ch == '/')
fputs ("::", stream);
else
jcf_print_char (stream, ch);
}
fputs (" *", stream);
if (*signature == ';')
signature++;
*need_space = 0;
break;
default:
*need_space = 1;
jni_print_char (stream, *signature++);
break;
printit:
signature++;
*need_space = 1;
fputs (ctype, stream);
break;
}
if (! flag_jni)
{
while (array_depth-- > 0)
fputs ("> *", stream);
}
return signature;
}
static void
DEFUN(print_c_decl, (stream, jcf, name_index, signature_index, is_init,
name_override, flags),
FILE* stream AND JCF* jcf
AND int name_index AND int signature_index
AND int is_init AND const char *name_override AND int flags)
{
if (JPOOL_TAG (jcf, signature_index) != CONSTANT_Utf8)
{
fprintf (stream, "<not a UTF8 constant>");
found_error = 1;
}
else
{
int length = JPOOL_UTF_LENGTH (jcf, signature_index);
const unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index);
register const unsigned char *str = str0;
const unsigned char *limit = str + length;
int need_space = 0;
int is_method = str[0] == '(';
const unsigned char *next;
if (is_method && ! is_init)
{
while (str < limit)
{
int ch = *str++;
if (ch == ')')
break;
}
}
if (! is_method || ! is_init)
{
next = decode_signature_piece (stream, str, limit, &need_space);
if (! next)
{
fprintf (stderr, "unparseable signature: `%s'\n", str0);
found_error = 1;
return;
}
}
if (need_space)
fputs (" ", stream);
print_full_cxx_name (stream, jcf, name_index,
signature_index, is_init, name_override,
flags);
}
}
static void
DEFUN(print_full_cxx_name, (stream, jcf, name_index, signature_index,
is_init, name_override, flags),
FILE* stream AND JCF* jcf
AND int name_index AND int signature_index AND int is_init
AND const char *name_override AND int flags)
{
int length = JPOOL_UTF_LENGTH (jcf, signature_index);
const unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index);
register const unsigned char *str = str0;
const unsigned char *limit = str + length;
int need_space = 0;
int is_method = str[0] == '(';
const unsigned char *next;
if (name_override)
fputs (name_override, stream);
else if (name_index)
{
if (is_init)
print_base_classname (stream, jcf, jcf->this_class);
else
print_name (stream, jcf, name_index);
}
if (flag_jni)
{
unsigned char *signature = JPOOL_UTF_DATA (jcf, signature_index);
int sig_len = JPOOL_UTF_LENGTH (jcf, signature_index);
if (overloaded_jni_method_exists_p (JPOOL_UTF_DATA (jcf, name_index),
JPOOL_UTF_LENGTH (jcf, name_index),
signature, sig_len))
{
unsigned char *limit = signature + sig_len;
fputs ("__", stream);
while (signature < limit)
{
int ch = UTF8_GET (signature, limit);
jni_print_char (stream, ch);
if (ch == ')')
{
break;
}
}
}
}
if (is_method)
{
fputs (" (", stream);
str = str0 + 1;
if (flag_jni)
{
fputs ("JNIEnv *env", stream);
fputs ((flags & ACC_STATIC) ? ", jclass" : ", jobject", stream);
if (*str != ')')
fputs (", ", stream);
}
while (str < limit && *str != ')')
{
next = decode_signature_piece (stream, str, limit, &need_space);
if (! next)
{
fprintf (stderr, "unparseable signature: `%s'\n", str0);
found_error = 1;
return;
}
if (next < limit && *next != ')')
fputs (", ", stream);
str = next;
}
fputs (")", stream);
}
}
static void
DEFUN (print_name_for_stub_or_jni, (stream, jcf, name_index, signature_index,
is_init, name_override, flags),
FILE *stream AND JCF *jcf
AND int name_index AND int signature_index
AND int is_init AND const char *name_override AND int flags)
{
const char *const prefix = flag_jni ? "Java_" : "";
print_cxx_classname (stream, prefix, jcf, jcf->this_class);
fputs (flag_jni ? "_" : "::", stream);
print_full_cxx_name (stream, jcf, name_index,
signature_index, is_init, name_override,
flags);
}
static void
DEFUN(print_stub_or_jni, (stream, jcf, name_index, signature_index, is_init,
name_override, flags),
FILE* stream AND JCF* jcf
AND int name_index AND int signature_index
AND int is_init AND const char *name_override AND int flags)
{
if (JPOOL_TAG (jcf, signature_index) != CONSTANT_Utf8)
{
fprintf (stream, "<not a UTF8 constant>");
found_error = 1;
}
else
{
int length = JPOOL_UTF_LENGTH (jcf, signature_index);
const unsigned char *str0 = JPOOL_UTF_DATA (jcf, signature_index);
register const unsigned char *str = str0;
const unsigned char *limit = str + length;
int need_space = 0;
int is_method = str[0] == '(';
const unsigned char *next;
if (! is_method && flag_jni)
return;
if (flag_jni && ! stubs)
fputs ("extern JNIEXPORT ", stream);
if (is_method && ! is_init)
{
while (str < limit)
{
int ch = *str++;
if (ch == ')')
break;
}
}
if (! is_method || ! is_init)
{
next = decode_signature_piece (stream, str, limit, &need_space);
if (! next)
{
fprintf (stderr, "unparseable signature: `%s'\n", str0);
found_error = 1;
return;
}
}
fputs (need_space && ! stubs ? " " : "\n", stream);
if (flag_jni && ! stubs)
fputs ("JNICALL ", stream);
print_name_for_stub_or_jni (stream, jcf, name_index,
signature_index, is_init, name_override,
flags);
if (stubs)
{
if (flag_jni)
fputs ("\n{\n (*env)->FatalError (\"", stream);
else
fputs ("\n{\n throw new ::java::lang::UnsupportedOperationException (JvNewStringLatin1 (\"", stream);
print_name_for_stub_or_jni (stream, jcf, name_index,
signature_index, is_init,
name_override,
flags);
fprintf (stream, " not implemented\")%s;\n}\n\n",
flag_jni ? "" : ")");
}
}
}
static void
DEFUN(print_mangled_classname, (stream, jcf, prefix, index),
FILE *stream AND JCF *jcf AND const char *prefix AND int index)
{
int name_index = JPOOL_USHORT1 (jcf, index);
fputs (prefix, stream);
jcf_print_utf8_replace (out,
JPOOL_UTF_DATA (jcf, name_index),
JPOOL_UTF_LENGTH (jcf, name_index),
'/', '_');
}
static int
print_cxx_classname (stream, prefix, jcf, index)
FILE *stream;
const char *prefix;
JCF *jcf;
int index;
{
int name_index = JPOOL_USHORT1 (jcf, index);
int len, c;
const unsigned char *s, *p, *limit;
s = JPOOL_UTF_DATA (jcf, name_index);
len = JPOOL_UTF_LENGTH (jcf, name_index);
limit = s + len;
p = s;
c = UTF8_GET (p, limit);
if (c == '[')
return 0;
fputs (prefix, stream);
if (! flag_jni && ! stubs)
fputs ("::", stream);
while (s < limit)
{
c = UTF8_GET (s, limit);
if (c == '/')
fputs (flag_jni ? "_" : "::", stream);
else
jni_print_char (stream, c);
}
return 1;
}
int written_class_count = 0;
static const unsigned char *
super_class_name (derived_jcf, len)
JCF *derived_jcf;
int *len;
{
int supername_index = JPOOL_USHORT1 (derived_jcf, derived_jcf->super_class);
int supername_length = JPOOL_UTF_LENGTH (derived_jcf, supername_index);
const unsigned char *supername =
JPOOL_UTF_DATA (derived_jcf, supername_index);
if (len)
*len = supername_length;
return supername;
}
struct include
{
char *name;
struct include *next;
};
static struct include *all_includes = NULL;
static void
print_include (out, utf8, len)
FILE *out;
const unsigned char *utf8;
int len;
{
struct include *incl;
if (! out)
return;
if (len == -1)
len = strlen (utf8);
for (incl = all_includes; incl; incl = incl->next)
{
if (len == (int) strlen (incl->name)
&& ! strncmp (incl->name, utf8, len))
return;
}
incl = xmalloc (sizeof (struct include));
incl->name = xmalloc (len + 1);
strncpy (incl->name, utf8, len);
incl->name[len] = '\0';
incl->next = all_includes;
all_includes = incl;
fputs ("#include <", out);
jcf_print_utf8_replace (out, utf8, len,
'/',
flag_jni ? '_' : '/');
fputs (".h>\n", out);
}
struct namelet
{
char *name;
int is_class;
struct namelet *subnamelets;
struct namelet *next;
};
static void add_namelet PARAMS ((const unsigned char *,
const unsigned char *, struct namelet *));
static void print_namelet PARAMS ((FILE *, struct namelet *, int));
static struct namelet root =
{
NULL,
0,
NULL,
NULL
};
static void
add_namelet (name, name_limit, parent)
const unsigned char *name, *name_limit;
struct namelet *parent;
{
const unsigned char *p;
struct namelet *n = NULL, *np;
if (parent == &root)
{
#define JAVALANG "java/lang/"
#define JAVAIO "java/io/"
#define JAVAUTIL "java/util/"
if ((name_limit - name >= (int) sizeof (JAVALANG) - 1
&& ! strncmp (name, JAVALANG, sizeof (JAVALANG) - 1))
|| (name_limit - name >= (int) sizeof (JAVAUTIL) - 1
&& ! strncmp (name, JAVAUTIL, sizeof (JAVAUTIL) - 1))
|| (name_limit - name >= (int) sizeof (JAVAIO) - 1
&& ! strncmp (name, JAVAIO, sizeof (JAVAIO) - 1)))
return;
}
for (p = name; p < name_limit && *p != '/'; ++p)
;
for (np = parent->subnamelets; np != NULL; np = np->next)
{
if ((int) strlen (np->name) == p - name &&
! strncmp (name, np->name, p - name))
{
n = np;
break;
}
}
if (n == NULL)
{
n = xmalloc (sizeof (struct namelet));
n->name = xmalloc (p - name + 1);
strncpy (n->name, name, p - name);
n->name[p - name] = '\0';
n->is_class = (p == name_limit);
n->subnamelets = NULL;
n->next = parent->subnamelets;
parent->subnamelets = n;
}
if (p < name_limit)
add_namelet (p + 1, name_limit, n);
}
static void
print_namelet (out, name, depth)
FILE *out;
struct namelet *name;
int depth;
{
int i, term = 0;
struct namelet *c;
if (name->name)
{
for (i = 0; i < depth; ++i)
fputc (' ', out);
fprintf (out, "%s %s", name->is_class ? "class" : "namespace",
name->name);
if (name->is_class && name->subnamelets == NULL)
fputs (";\n", out);
else
{
term = 1;
fputs ("\n", out);
for (i = 0; i < depth; ++i)
fputc (' ', out);
fputs ("{\n", out);
}
}
c = name->subnamelets;
while (c != NULL)
{
struct namelet *next = c->next;
print_namelet (out, c, depth + 2);
c = next;
}
name->subnamelets = NULL;
if (name->name)
{
if (term)
{
for (i = 0; i < depth; ++i)
fputc (' ', out);
fputs ("}\n", out);
if (name->is_class)
fputs (";", out);
}
free (name->name);
free (name);
}
}
static void
add_class_decl (out, jcf, signature)
FILE *out;
JCF *jcf;
JCF_u2 signature;
{
const unsigned char *s = JPOOL_UTF_DATA (jcf, signature);
int len = JPOOL_UTF_LENGTH (jcf, signature);
int i;
for (i = 0; i < len; ++i)
{
int start;
if (s[i] == '[')
{
print_include (out, "gcj/array", -1);
continue;
}
if (s[i] != 'L')
continue;
for (start = ++i; i < len && s[i] != ';'; ++i)
;
add_namelet (&s[start], &s[i], &root);
}
}
static void
print_class_decls (out, jcf, self)
FILE *out;
JCF *jcf;
int self;
{
int name_index = JPOOL_USHORT1 (jcf, self);
int len;
const unsigned char *s;
s = JPOOL_UTF_DATA (jcf, name_index);
len = JPOOL_UTF_LENGTH (jcf, name_index);
add_namelet (s, s + len, &root);
if (root.subnamelets)
{
fputs ("extern \"Java\"\n{\n", out);
print_namelet (out, &root, 0);
fputs ("};\n\n", out);
}
}
static void
DEFUN(process_file, (jcf, out),
JCF *jcf AND FILE *out)
{
int code, i;
uint32 field_start, method_end, method_start;
current_jcf = jcf;
last_access = -1;
if (jcf_parse_preamble (jcf) != 0)
{
fprintf (stderr, "Not a valid Java .class file.\n");
found_error = 1;
return;
}
code = jcf_parse_constant_pool (jcf);
if (code != 0)
{
fprintf (stderr, "error while parsing constant pool\n");
found_error = 1;
return;
}
code = verify_constant_pool (jcf);
if (code > 0)
{
fprintf (stderr, "error in constant pool entry #%d\n", code);
found_error = 1;
return;
}
jcf_parse_class (jcf);
if (written_class_count++ == 0 && out)
{
const char *cstart, *cstart2, *mode, *cend, *what, *jflag;
if (flag_jni)
{
cstart = "/*";
cstart2 = " ";
cend = " */";
mode = "";
what = "JNI";
jflag = " -jni";
}
else
{
cstart = "//";
cstart2 = "//";
cend = "";
mode = " -*- c++ -*-";
what = "CNI";
jflag = "";
}
if (! stubs)
fprintf (out, "%s DO NOT EDIT THIS FILE - it is machine generated%s%s\n\n",
cstart, mode, cend);
else
{
fprintf (out, "%s This file was created by `gcjh -stubs%s'.%s\n\
%s\n\
%s This file is intended to give you a head start on implementing native\n\
%s methods using %s.\n\
%s Be aware: running `gcjh -stubs %s' once more for this class may\n\
%s overwrite any edits you have made to this file.%s\n\n",
cstart, jflag, mode,
cstart2,
cstart2,
cstart2,
what,
cstart2,
jflag,
cstart2,
cend);
}
}
if (out)
{
if (! stubs)
{
print_mangled_classname (out, jcf, "#ifndef __", jcf->this_class);
fprintf (out, "__\n");
print_mangled_classname (out, jcf, "#define __", jcf->this_class);
fprintf (out, "__\n\n");
if (flag_jni)
{
fprintf (out, "#include <jni.h>\n\n");
fprintf (out, "#ifdef __cplusplus\n");
fprintf (out, "extern \"C\"\n");
fprintf (out, "{\n");
fprintf (out, "#endif\n");
}
else
{
fprintf (out, "#pragma interface\n");
if (jcf->super_class)
{
int super_length;
const unsigned char *supername =
super_class_name (jcf, &super_length);
fputs ("\n", out);
print_include (out, supername, super_length);
}
}
}
else
{
char *name;
int i, len = strlen (jcf->classname);
if (len > 6 && ! strcmp (&jcf->classname[len - 6], ".class"))
len -= 6;
name = xmalloc (len + 1);
for (i = 0; i < len; ++i)
name[i] = jcf->classname[i] == '.' ? '/' : jcf->classname[i];
name[i] = '\0';
print_include (out, name, len);
free (name);
if (! flag_jni)
{
print_include (out, "gcj/cni", -1);
print_include (out, "java/lang/UnsupportedOperationException",
-1);
}
}
}
field_pass = 0;
field_start = JCF_TELL (jcf);
jcf_parse_fields (jcf);
method_start = JCF_TELL (jcf);
method_pass = 0;
jcf_parse_methods (jcf);
if (out)
fputs ("\n", out);
if (out && ! flag_jni)
{
if (! stubs)
print_class_decls (out, jcf, jcf->this_class);
for (i = 0; i < prepend_count; ++i)
fprintf (out, "%s\n", prepend_specs[i]);
if (prepend_count > 0)
fputc ('\n', out);
if (! stubs)
{
if (! print_cxx_classname (out, "class ", jcf, jcf->this_class))
{
fprintf (stderr, "class is of array type\n");
found_error = 1;
return;
}
if (jcf->super_class)
{
if (! print_cxx_classname (out, " : public ",
jcf, jcf->super_class))
{
fprintf (stderr, "base class is of array type\n");
found_error = 1;
return;
}
}
fputs ("\n{\n", out);
}
}
JCF_SEEK (jcf, method_start);
method_pass = 1;
jcf_parse_methods (jcf);
method_end = JCF_TELL (jcf);
field_pass = 1;
JCF_SEEK (jcf, field_start);
jcf_parse_fields (jcf);
JCF_SEEK (jcf, method_end);
jcf_parse_final_attributes (jcf);
if (out && ! stubs)
{
if (flag_jni)
{
fprintf (out, "\n#ifdef __cplusplus\n");
fprintf (out, "}\n");
fprintf (out, "#endif\n");
}
else
{
for (i = 0; i < friend_count; ++i)
fprintf (out, " friend %s\n", friend_specs[i]);
if (add_count > 0)
fputc ('\n', out);
for (i = 0; i < add_count; ++i)
fprintf (out, " %s\n", add_specs[i]);
generate_access (out, ACC_PUBLIC);
fprintf (out, "\n static ::java::lang::Class class$;\n");
fputs ("}", out);
if (jcf->access_flags & ACC_INTERFACE)
fputs (" __attribute__ ((java_interface))", out);
fputs (";\n", out);
if (append_count > 0)
fputc ('\n', out);
for (i = 0; i < append_count; ++i)
fprintf (out, "%s\n", append_specs[i]);
}
print_mangled_classname (out, jcf,
"\n#endif /* __", jcf->this_class);
fprintf (out, "__ */\n");
}
}
#define LONG_OPT(Num) ((Num) + 128)
#define OPT_classpath LONG_OPT (0)
#define OPT_CLASSPATH OPT_classpath
#define OPT_bootclasspath LONG_OPT (1)
#define OPT_extdirs LONG_OPT (2)
#define OPT_HELP LONG_OPT (3)
#define OPT_TEMP LONG_OPT (4)
#define OPT_VERSION LONG_OPT (5)
#define OPT_PREPEND LONG_OPT (6)
#define OPT_FRIEND LONG_OPT (7)
#define OPT_ADD LONG_OPT (8)
#define OPT_APPEND LONG_OPT (9)
#define OPT_M LONG_OPT (10)
#define OPT_MM LONG_OPT (11)
#define OPT_MG LONG_OPT (12)
#define OPT_MD LONG_OPT (13)
#define OPT_MMD LONG_OPT (14)
static const struct option options[] =
{
{ "classpath", required_argument, NULL, OPT_classpath },
{ "bootclasspath", required_argument, NULL, OPT_bootclasspath },
{ "extdirs", required_argument, NULL, OPT_extdirs },
{ "CLASSPATH", required_argument, NULL, OPT_CLASSPATH },
{ "help", no_argument, NULL, OPT_HELP },
{ "stubs", no_argument, &stubs, 1 },
{ "td", required_argument, NULL, OPT_TEMP },
{ "verbose", no_argument, NULL, 'v' },
{ "version", no_argument, NULL, OPT_VERSION },
{ "prepend", required_argument, NULL, OPT_PREPEND },
{ "friend", required_argument, NULL, OPT_FRIEND },
{ "add", required_argument, NULL, OPT_ADD },
{ "append", required_argument, NULL, OPT_APPEND },
{ "M", no_argument, NULL, OPT_M },
{ "MM", no_argument, NULL, OPT_MM },
{ "MG", no_argument, NULL, OPT_MG },
{ "MD", no_argument, NULL, OPT_MD },
{ "MMD", no_argument, NULL, OPT_MMD },
{ "jni", no_argument, &flag_jni, 1 },
{ NULL, no_argument, NULL, 0 }
};
static void
usage ()
{
fprintf (stderr, "Try `gcjh --help' for more information.\n");
exit (1);
}
static void
help ()
{
printf ("Usage: gcjh [OPTION]... CLASS...\n\n");
printf ("Generate C++ header files from .class files\n\n");
printf (" -stubs Generate an implementation stub file\n");
printf (" -jni Generate a JNI header or stub\n");
printf ("\n");
printf (" -add TEXT Insert TEXT into class body\n");
printf (" -append TEXT Insert TEXT after class declaration\n");
printf (" -friend TEXT Insert TEXT as `friend' declaration\n");
printf (" -prepend TEXT Insert TEXT before start of class\n");
printf ("\n");
printf (" --classpath PATH Set path to find .class files\n");
printf (" -IDIR Append directory to class path\n");
printf (" --bootclasspath PATH Override built-in class path\n");
printf (" --extdirs PATH Set extensions directory path\n");
printf (" -d DIRECTORY Set output directory name\n");
printf (" -o FILE Set output file name\n");
printf (" -td DIRECTORY Set temporary directory name\n");
printf ("\n");
printf (" --help Print this help, then exit\n");
printf (" --version Print version number, then exit\n");
printf (" -v, --verbose Print extra information while running\n");
printf ("\n");
printf (" -M Print all dependencies to stdout;\n");
printf (" suppress ordinary output\n");
printf (" -MM Print non-system dependencies to stdout;\n");
printf (" suppress ordinary output\n");
printf (" -MD Print all dependencies to stdout\n");
printf (" -MMD Print non-system dependencies to stdout\n");
printf ("\n");
printf ("For bug reporting instructions, please see:\n");
printf ("%s.\n", bug_report_url);
exit (0);
}
static void
version ()
{
printf ("gcjh (GCC) %s\n\n", version_string);
printf ("Copyright (C) 2002 Free Software Foundation, Inc.\n");
printf ("This is free software; see the source for copying conditions. There is NO\n");
printf ("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
exit (0);
}
int
DEFUN(main, (argc, argv),
int argc AND char** argv)
{
JCF jcf;
int argi;
char *output_file = NULL;
int emit_dependencies = 0, suppress_output = 0;
int opt;
if (argc <= 1)
{
fprintf (stderr, "gcjh: no classes specified\n");
usage ();
}
jcf_path_init ();
while ((opt = getopt_long_only (argc, argv, "I:d:o:v", options, NULL)) != -1)
{
switch (opt)
{
case 0:
break;
case 'o':
output_file = optarg;
break;
case 'd':
output_directory = optarg;
break;
case 'I':
jcf_path_include_arg (optarg);
break;
case 'v':
verbose++;
break;
case OPT_classpath:
jcf_path_classpath_arg (optarg);
break;
case OPT_bootclasspath:
jcf_path_bootclasspath_arg (optarg);
break;
case OPT_extdirs:
jcf_path_extdirs_arg (optarg);
break;
case OPT_HELP:
help ();
break;
case OPT_TEMP:
temp_directory = optarg;
break;
case OPT_VERSION:
version ();
break;
case OPT_PREPEND:
if (prepend_count == 0)
prepend_specs = ALLOC (argc * sizeof (char*));
prepend_specs[prepend_count++] = optarg;
break;
case OPT_FRIEND:
if (friend_count == 0)
friend_specs = ALLOC (argc * sizeof (char*));
friend_specs[friend_count++] = optarg;
break;
case OPT_ADD:
if (add_count == 0)
add_specs = ALLOC (argc * sizeof (char*));
add_specs[add_count++] = optarg;
break;
case OPT_APPEND:
if (append_count == 0)
append_specs = ALLOC (argc * sizeof (char*));
append_specs[append_count++] = optarg;
break;
case OPT_M:
emit_dependencies = 1;
suppress_output = 1;
jcf_dependency_init (1);
break;
case OPT_MM:
emit_dependencies = 1;
suppress_output = 1;
jcf_dependency_init (0);
break;
case OPT_MG:
fprintf (stderr, "gcjh: `-MG' option is unimplemented\n");
exit (1);
case OPT_MD:
emit_dependencies = 1;
jcf_dependency_init (1);
break;
case OPT_MMD:
emit_dependencies = 1;
jcf_dependency_init (0);
break;
default:
usage ();
break;
}
}
if (optind == argc)
{
fprintf (stderr, "gcjh: no classes specified\n");
usage ();
}
jcf_path_seal (verbose);
if (output_file && emit_dependencies)
{
fprintf (stderr, "gcjh: can't specify both -o and -MD\n");
exit (1);
}
for (argi = optind; argi < argc; argi++)
{
char *classname = argv[argi];
char *current_output_file;
const char *classfile_name;
if (verbose)
fprintf (stderr, "Processing %s\n", classname);
if (! output_file)
jcf_dependency_reset ();
classfile_name = find_class (classname, strlen (classname), &jcf, 0);
if (classfile_name == NULL)
{
fprintf (stderr, "%s: no such class\n", classname);
exit (1);
}
if (verbose)
fprintf (stderr, "Found in %s\n", classfile_name);
if (output_file)
{
if (strcmp (output_file, "-") == 0)
out = stdout;
else if (out == NULL)
{
out = fopen (output_file, "w");
}
if (out == NULL)
{
perror (output_file);
exit (1);
}
current_output_file = output_file;
}
else
{
int dir_len = strlen (output_directory);
int i, classname_length = strlen (classname);
current_output_file = ALLOC (dir_len + classname_length + 5);
strcpy (current_output_file, output_directory);
if (dir_len > 0 && output_directory[dir_len-1] != '/')
current_output_file[dir_len++] = '/';
for (i = 0; classname[i] != '\0'; i++)
{
char ch = classname[i];
if (ch == '.')
ch = '/';
if (flag_jni && ch == '/')
ch = '_';
current_output_file[dir_len++] = ch;
}
if (emit_dependencies)
{
if (suppress_output)
{
jcf_dependency_set_dep_file ("-");
out = NULL;
}
else
{
strcpy (current_output_file + dir_len, ".hd");
jcf_dependency_set_dep_file (current_output_file);
}
}
strcpy (current_output_file + dir_len,
stubs ? (flag_jni ? ".c" : ".cc") : ".h");
jcf_dependency_set_target (current_output_file);
if (! suppress_output)
{
out = fopen (current_output_file, "w");
if (out == NULL)
{
perror (current_output_file);
exit (1);
}
}
}
process_file (&jcf, out);
JCF_FINISH (&jcf);
if (current_output_file != output_file)
free (current_output_file);
jcf_dependency_write ();
}
if (out != NULL && out != stdout)
fclose (out);
return found_error;
}