binutils-gdb/gdb/jv-valprint.c
Tom Tromey 79a45b7d67 gdb
* varobj.c (value_get_print_value): Include valprint.h.
	(value_get_print_value): Use get_formatted_print_options.
	* value.h (struct value_print_options): Declare.
	(value_print, val_print, common_val_print, val_print_string):
	Update.
	* value.c: Include valprint.h.
	(show_values): Use get_user_print_options.
	(show_convenience): Likewise.
	* valprint.h (prettyprint_arrays, prettyprint_structs): Don't
	declare.
	(struct value_print_options): New type.
	(vtblprint, unionprint, addressprint, objectprint, print_max,
	inspect_it, repeat_count_threshold, output_format,
	stop_print_at_null): Don't declare.
	(user_print_options, get_user_print_options,
	get_raw_print_options, get_formatted_print_options): Declare.
	(print_array_indexes_p): Don't declare.
	(maybe_print_array_index, val_print_array_elements): Update.
	* valprint.c (print_max): Remove.
	(user_print_options): New global.
	(get_user_print_options, get_raw_print_options,
	get_formatted_print_options): New functions.
	(print_array_indexes, repeat_count_threshold, stop_print_at_null,
	prettyprint_structs, prettyprint_arrays, unionprint,
	addressprint): Remove.
	(val_print): Remove format, deref_ref, pretty arguments; add
	options.  Update.
	(common_val_print): Likewise.
	(print_array_indexes_p): Remove.
	(maybe_print_array_index): Remove format, pretty arguments; add
	options.  Update.
	(val_print_array_elements): Remove format, deref_ref, pretty
	arguments; add options.  Update.
	(val_print_string): Add options argument.  Update.
	(_initialize_valprint): Use user_print_options.
	(output_format): Remove.
	(set_output_radix_1): Use user_print_options.
	* typeprint.c: Include valprint.h.
	(objectprint): Don't declare.
	(whatis_exp): Use get_user_print_options.
	* tui/tui-regs.c: Include valprint.h.
	(tui_register_format): Use get_formatted_print_options.
	* tracepoint.c: Include valprint.h.
	(addressprint): Don't declare.
	(trace_mention): Use get_user_print_options.
	(tracepoints_info): Likewise.
	* stack.c (print_frame_args): Use get_raw_print_options.
	(print_frame_info): Use get_user_print_options.
	(print_frame): Likewise.
	* sh64-tdep.c: Include valprint.h
	(sh64_do_register): Use get_formatted_print_options.
	* scm-valprint.c (scm_inferior_print): Remove format, deref_ref,
	pretty arguments; add options.
	(scm_scmlist_print): Likewise.  Update.
	(scm_scmval_print): Likewise.
	(scm_val_print): Likewise.
	(scm_value_print): Remove format, pretty arguments; add options.
	Update.
	* scm-lang.h (scm_value_print, scm_val_print, scm_scmval_print):
	Update.
	* scm-lang.c (scm_printstr): Add options argument.
	* python/python-value.c: Include valprint.h.
	(valpy_str): Use get_user_print_options.
	* printcmd.c: Include valprint.h.
	(addressprint): Don't declare.
	(inspect_it): Remove.
	(print_formatted): Remove format option; add options.  Update.
	(print_scalar_formatted): Likewise.
	(print_address_demangle): Use get_user_print_options.
	(do_examine): Use get_formatted_print_options.
	(print_command_1): Likewise.
	(output_command): Use get_formatted_print_options.
	(do_one_display): Likewise.
	(print_variable_value): Use get_user_print_options.
	* p-valprint.c (pascal_val_print): Remove format, deref_ref,
	pretty arguments; add options.  Update.
	(pascal_value_print): Remove format, pretty arguments; add
	options.  Update.
	(vtblprint, objectprint): Don't declare.
	(pascal_static_field_print): Remove.
	(pascal_object_print_value_fields): Remove format, pretty
	arguments; add options.  Update.
	(pascal_object_print_static_field): Likewise.
	(_initialize_pascal_valprint): Use user_print_options.  Update.
	* p-lang.h (pascal_val_print, pascal_value_print,
	pascal_printstr, pascal_object_print_value_fields): Update.
	(vtblprint, static_field_print): Don't declare.
	* p-lang.c (pascal_printstr): Add options argument.  Update.
	* objc-lang.c (objc_printstr): Add options argument.  Update.
	* mt-tdep.c: Include valprint.h.
	(mt_registers_info): Use get_raw_print_options.
	* mips-tdep.c: Include valprint.h.
	(mips_print_fp_register): Use get_formatted_print_options.
	(mips_print_register): Likewise.
	* mi/mi-main.c: Include valprint.h.
	(get_register): Use get_user_print_options.
	(mi_cmd_data_evaluate_expression): Likewise.
	(mi_cmd_data_read_memory): Use get_formatted_print_options.
	* mi/mi-cmd-stack.c: Include valprint.h.
	(list_args_or_locals): Use get_raw_print_options.
	* m2-valprint.c (print_function_pointer_address): Add addressprint
	argument.
	(m2_print_long_set): Remove format, pretty arguments.
	(m2_print_unbounded_array): Remove format, deref_ref, pretty
	arguments; add options.  Update.
	(print_unpacked_pointer): Remove format argument; add options.
	Now static.  Update.
	(print_variable_at_address): Remove format, deref_ref, pretty
	arguments; add options.  Update.
	(m2_print_array_contents): Likewise.
	(m2_val_print): Likewise.
	* m2-lang.h (m2_val_print): Update.
	* m2-lang.c (m2_printstr): Add options argument. Update.
	* language.h (struct value_print_options): Declare.
	(struct language_defn) <la_printstr>: Add options argument.
	<la_val_print>: Remove format, deref_ref, pretty argument; add
	options.
	<la_value_print>: Remove format, pretty arguments; add options.
	<la_print_array_index>: Likewise.
	(LA_VAL_PRINT, LA_VALUE_PRINT, LA_PRINT_STRING,
	LA_PRINT_ARRAY_INDEX): Update.
	(default_print_array_index): Update.
	* language.c (default_print_array_index): Remove format, pretty
	arguments; add options.  Update.
	(unk_lang_printstr): Add options argument.
	(unk_lang_val_print): Remove format, deref_ref, pretty arguments;
	add options.
	(unk_lang_value_print): Remove format, pretty arguments; add
	options.
	* jv-valprint.c (java_value_print): Remove format, pretty
	arguments; add options.  Update.
	(java_print_value_fields): Likewise.
	(java_val_print): Remove format, deref_ref, pretty arguments; add
	options.  Update.
	* jv-lang.h (java_val_print, java_value_print): Declare.
	* infcmd.c: Include valprint.h.
	(print_return_value): Use get_raw_print_options.
	(default_print_registers_info): Use get_user_print_options,
	get_formatted_print_options.
	(registers_info): Use get_formatted_print_options.
	* gdbtypes.h (struct value_print_options): Declare.
	(print_scalar_formatted): Update.
	* f-valprint.c (f77_print_array_1): Remove format, deref_ref,
	pretty arguments; add options.  Update.
	(f77_print_array): Likewise.
	(f_val_print): Likewise.
	* f-lang.h (f_val_print): Update.
	* f-lang.c (f_printstr): Add options argument.  Update.
	(c_value_print): Update declaration.
	* expprint.c: Include valprint.h.
	(print_subexp_standard): Use get_raw_print_options,
	get_user_print_options.
	* eval.c: Include valprint.h.
	(objectprint): Don't declare.
	(evaluate_subexp_standard): Use get_user_print_options.
	* cp-valprint.c (vtblprint, objectprint, static_field_print):
	Remove.
	(cp_print_value_fields): Remove format, pretty arguments; add
	options.  Update.
	(cp_print_value): Likewise.
	(cp_print_static_field): Likewise.
	(_initialize_cp_valprint): Use user_print_options.  Update.
	* c-valprint.c (print_function_pointer_address): Add addressprint
	argument.
	(c_val_print): Remove format, deref_ref, pretty arguments; add
	options.  Update.
	(c_value_print): Add options argument.  Update.
	* c-lang.h (c_val_print, c_value_print, c_printstr): Update.
	(vtblprint, static_field_print): Don't declare.
	(cp_print_value_fields): Update.
	* c-lang.c (c_printstr): Add options argument.  Update.
	* breakpoint.c: Include valprint.h.
	(addressprint): Don't declare.
	(watchpoint_value_print): Use get_user_print_options.
	(print_one_breakpoint_location): Likewise.
	(breakpoint_1, print_it_catch_fork, print_it_catch_vfork, mention,
	print_exception_catchpoint): Likewise.
	* auxv.c (fprint_target_auxv): Don't declare addressprint.  Use
	get_user_print_options.
	* ada-valprint.c (struct ada_val_print_args): Remove format,
	deref_ref, and pretty; add options.
	(print_optional_low_bound): Add options argument.
	(val_print_packed_array_elements): Remove format and pretty
	arguments; add options.  Update.
	(printstr): Add options argument.  Update.
	(ada_printstr): Likewise.
	(ada_val_print): Remove format, deref_ref, pretty arguments; add
	options argument.  Update.
	(ada_val_print_stub): Update.
	(ada_val_print_array): Remove format, deref_ref, pretty arguments;
	add options.  Update.
	(ada_val_print_1): Likewise.
	(print_variant_part): Likewise.
	(ada_value_print): Remove format, pretty arguments; add options.
	Update.
	(print_record): Likewise.
	(print_field_values): Likewise.
	* ada-lang.h (ada_val_print, ada_value_print, ada_printstr):
	Update.
	* ada-lang.c (ada_print_array_index): Add options argument; remove
	format and pretty arguments.
	(print_one_exception): Use get_user_print_options.
gdb/testsuite
	* gdb.base/exprs.exp (test_expr): Add enum formatting tests.
2008-10-28 17:19:58 +00:00

540 lines
15 KiB
C

/* Support for printing Java values for GDB, the GNU debugger.
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007,
2008 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "symtab.h"
#include "gdbtypes.h"
#include "gdbcore.h"
#include "expression.h"
#include "value.h"
#include "demangle.h"
#include "valprint.h"
#include "language.h"
#include "jv-lang.h"
#include "c-lang.h"
#include "annotate.h"
#include "gdb_string.h"
/* Local functions */
int
java_value_print (struct value *val, struct ui_file *stream,
const struct value_print_options *options)
{
struct type *type;
CORE_ADDR address;
int i;
char *name;
struct value_print_options opts;
type = value_type (val);
address = VALUE_ADDRESS (val) + value_offset (val);
if (is_object_type (type))
{
CORE_ADDR obj_addr;
/* Get the run-time type, and cast the object into that */
obj_addr = unpack_pointer (type, value_contents (val));
if (obj_addr != 0)
{
type = type_from_class (java_class_from_object (val));
type = lookup_pointer_type (type);
val = value_at (type, address);
}
}
if (TYPE_CODE (type) == TYPE_CODE_PTR && !value_logical_not (val))
type_print (TYPE_TARGET_TYPE (type), "", stream, -1);
name = TYPE_TAG_NAME (type);
if (TYPE_CODE (type) == TYPE_CODE_STRUCT && name != NULL
&& (i = strlen (name), name[i - 1] == ']'))
{
gdb_byte buf4[4];
long length;
unsigned int things_printed = 0;
int reps;
struct type *el_type = java_primitive_type_from_name (name, i - 2);
i = 0;
read_memory (address + JAVA_OBJECT_SIZE, buf4, 4);
length = (long) extract_signed_integer (buf4, 4);
fprintf_filtered (stream, "{length: %ld", length);
if (el_type == NULL)
{
CORE_ADDR element;
CORE_ADDR next_element = -1; /* dummy initial value */
address += JAVA_OBJECT_SIZE + 4; /* Skip object header and length. */
while (i < length && things_printed < options->print_max)
{
gdb_byte *buf;
buf = alloca (gdbarch_ptr_bit (current_gdbarch) / HOST_CHAR_BIT);
fputs_filtered (", ", stream);
wrap_here (n_spaces (2));
if (i > 0)
element = next_element;
else
{
read_memory (address, buf, sizeof (buf));
address += gdbarch_ptr_bit (current_gdbarch) / HOST_CHAR_BIT;
/* FIXME: cagney/2003-05-24: Bogus or what. It
pulls a host sized pointer out of the target and
then extracts that as an address (while assuming
that the address is unsigned)! */
element = extract_unsigned_integer (buf, sizeof (buf));
}
for (reps = 1; i + reps < length; reps++)
{
read_memory (address, buf, sizeof (buf));
address += gdbarch_ptr_bit (current_gdbarch) / HOST_CHAR_BIT;
/* FIXME: cagney/2003-05-24: Bogus or what. It
pulls a host sized pointer out of the target and
then extracts that as an address (while assuming
that the address is unsigned)! */
next_element = extract_unsigned_integer (buf, sizeof (buf));
if (next_element != element)
break;
}
if (reps == 1)
fprintf_filtered (stream, "%d: ", i);
else
fprintf_filtered (stream, "%d..%d: ", i, i + reps - 1);
if (element == 0)
fprintf_filtered (stream, "null");
else
fprintf_filtered (stream, "@%s", paddr_nz (element));
things_printed++;
i += reps;
}
}
else
{
struct value *v = allocate_value (el_type);
struct value *next_v = allocate_value (el_type);
VALUE_ADDRESS (v) = address + JAVA_OBJECT_SIZE + 4;
VALUE_ADDRESS (next_v) = VALUE_ADDRESS (v);
while (i < length && things_printed < options->print_max)
{
fputs_filtered (", ", stream);
wrap_here (n_spaces (2));
if (i > 0)
{
struct value *tmp;
tmp = next_v;
next_v = v;
v = tmp;
}
else
{
set_value_lazy (v, 1);
set_value_offset (v, 0);
}
set_value_offset (next_v, value_offset (v));
for (reps = 1; i + reps < length; reps++)
{
set_value_lazy (next_v, 1);
set_value_offset (next_v, value_offset (next_v) + TYPE_LENGTH (el_type));
if (memcmp (value_contents (v), value_contents (next_v),
TYPE_LENGTH (el_type)) != 0)
break;
}
if (reps == 1)
fprintf_filtered (stream, "%d: ", i);
else
fprintf_filtered (stream, "%d..%d: ", i, i + reps - 1);
opts = *options;
opts.deref_ref = 1;
common_val_print (v, stream, 1, &opts, current_language);
things_printed++;
i += reps;
}
}
if (i < length)
fprintf_filtered (stream, "...");
fprintf_filtered (stream, "}");
return 0;
}
/* If it's type String, print it */
if (TYPE_CODE (type) == TYPE_CODE_PTR
&& TYPE_TARGET_TYPE (type)
&& TYPE_TAG_NAME (TYPE_TARGET_TYPE (type))
&& strcmp (TYPE_TAG_NAME (TYPE_TARGET_TYPE (type)),
"java.lang.String") == 0
&& (options->format == 0 || options->format == 's')
&& address != 0
&& value_as_address (val) != 0)
{
struct value *data_val;
CORE_ADDR data;
struct value *boffset_val;
unsigned long boffset;
struct value *count_val;
unsigned long count;
struct value *mark;
mark = value_mark (); /* Remember start of new values */
data_val = value_struct_elt (&val, NULL, "data", NULL, NULL);
data = value_as_address (data_val);
boffset_val = value_struct_elt (&val, NULL, "boffset", NULL, NULL);
boffset = value_as_address (boffset_val);
count_val = value_struct_elt (&val, NULL, "count", NULL, NULL);
count = value_as_address (count_val);
value_free_to_mark (mark); /* Release unnecessary values */
val_print_string (data + boffset, count, 2, stream, options);
return 0;
}
opts = *options;
opts.deref_ref = 1;
return common_val_print (val, stream, 0, &opts, current_language);
}
/* TYPE, VALADDR, ADDRESS, STREAM, RECURSE, and OPTIONS have the
same meanings as in cp_print_value and c_val_print.
DONT_PRINT is an array of baseclass types that we
should not print, or zero if called from top level. */
static void
java_print_value_fields (struct type *type, const gdb_byte *valaddr,
CORE_ADDR address, struct ui_file *stream,
int recurse,
const struct value_print_options *options)
{
int i, len, n_baseclasses;
CHECK_TYPEDEF (type);
fprintf_filtered (stream, "{");
len = TYPE_NFIELDS (type);
n_baseclasses = TYPE_N_BASECLASSES (type);
if (n_baseclasses > 0)
{
int i, n_baseclasses = TYPE_N_BASECLASSES (type);
for (i = 0; i < n_baseclasses; i++)
{
int boffset;
struct type *baseclass = check_typedef (TYPE_BASECLASS (type, i));
char *basename = TYPE_NAME (baseclass);
const gdb_byte *base_valaddr;
if (BASETYPE_VIA_VIRTUAL (type, i))
continue;
if (basename != NULL && strcmp (basename, "java.lang.Object") == 0)
continue;
boffset = 0;
if (options->pretty)
{
fprintf_filtered (stream, "\n");
print_spaces_filtered (2 * (recurse + 1), stream);
}
fputs_filtered ("<", stream);
/* Not sure what the best notation is in the case where there is no
baseclass name. */
fputs_filtered (basename ? basename : "", stream);
fputs_filtered ("> = ", stream);
base_valaddr = valaddr;
java_print_value_fields (baseclass, base_valaddr, address + boffset,
stream, recurse + 1, options);
fputs_filtered (", ", stream);
}
}
if (!len && n_baseclasses == 1)
fprintf_filtered (stream, "<No data fields>");
else
{
int fields_seen = 0;
for (i = n_baseclasses; i < len; i++)
{
/* If requested, skip printing of static fields. */
if (field_is_static (&TYPE_FIELD (type, i)))
{
char *name = TYPE_FIELD_NAME (type, i);
if (!options->static_field_print)
continue;
if (name != NULL && strcmp (name, "class") == 0)
continue;
}
if (fields_seen)
fprintf_filtered (stream, ", ");
else if (n_baseclasses > 0)
{
if (options->pretty)
{
fprintf_filtered (stream, "\n");
print_spaces_filtered (2 + 2 * recurse, stream);
fputs_filtered ("members of ", stream);
fputs_filtered (type_name_no_tag (type), stream);
fputs_filtered (": ", stream);
}
}
fields_seen = 1;
if (options->pretty)
{
fprintf_filtered (stream, "\n");
print_spaces_filtered (2 + 2 * recurse, stream);
}
else
{
wrap_here (n_spaces (2 + 2 * recurse));
}
if (options->inspect_it)
{
if (TYPE_CODE (TYPE_FIELD_TYPE (type, i)) == TYPE_CODE_PTR)
fputs_filtered ("\"( ptr \"", stream);
else
fputs_filtered ("\"( nodef \"", stream);
if (field_is_static (&TYPE_FIELD (type, i)))
fputs_filtered ("static ", stream);
fprintf_symbol_filtered (stream, TYPE_FIELD_NAME (type, i),
language_cplus,
DMGL_PARAMS | DMGL_ANSI);
fputs_filtered ("\" \"", stream);
fprintf_symbol_filtered (stream, TYPE_FIELD_NAME (type, i),
language_cplus,
DMGL_PARAMS | DMGL_ANSI);
fputs_filtered ("\") \"", stream);
}
else
{
annotate_field_begin (TYPE_FIELD_TYPE (type, i));
if (field_is_static (&TYPE_FIELD (type, i)))
fputs_filtered ("static ", stream);
fprintf_symbol_filtered (stream, TYPE_FIELD_NAME (type, i),
language_cplus,
DMGL_PARAMS | DMGL_ANSI);
annotate_field_name_end ();
fputs_filtered (": ", stream);
annotate_field_value ();
}
if (!field_is_static (&TYPE_FIELD (type, i))
&& TYPE_FIELD_PACKED (type, i))
{
struct value *v;
/* Bitfields require special handling, especially due to byte
order problems. */
if (TYPE_FIELD_IGNORE (type, i))
{
fputs_filtered ("<optimized out or zero length>", stream);
}
else
{
struct value_print_options opts;
v = value_from_longest (TYPE_FIELD_TYPE (type, i),
unpack_field_as_long (type, valaddr, i));
opts = *options;
opts.deref_ref = 0;
common_val_print (v, stream, recurse + 1,
&opts, current_language);
}
}
else
{
if (TYPE_FIELD_IGNORE (type, i))
{
fputs_filtered ("<optimized out or zero length>", stream);
}
else if (field_is_static (&TYPE_FIELD (type, i)))
{
struct value *v = value_static_field (type, i);
if (v == NULL)
fputs_filtered ("<optimized out>", stream);
else
{
struct value_print_options opts;
struct type *t = check_typedef (value_type (v));
if (TYPE_CODE (t) == TYPE_CODE_STRUCT)
v = value_addr (v);
opts = *options;
opts.deref_ref = 0;
common_val_print (v, stream, recurse + 1,
&opts, current_language);
}
}
else if (TYPE_FIELD_TYPE (type, i) == NULL)
fputs_filtered ("<unknown type>", stream);
else
{
struct value_print_options opts = *options;
opts.deref_ref = 0;
val_print (TYPE_FIELD_TYPE (type, i),
valaddr + TYPE_FIELD_BITPOS (type, i) / 8, 0,
address + TYPE_FIELD_BITPOS (type, i) / 8,
stream, recurse + 1, &opts,
current_language);
}
}
annotate_field_end ();
}
if (options->pretty)
{
fprintf_filtered (stream, "\n");
print_spaces_filtered (2 * recurse, stream);
}
}
fprintf_filtered (stream, "}");
}
/* Print data of type TYPE located at VALADDR (within GDB), which came from
the inferior at address ADDRESS, onto stdio stream STREAM according to
OPTIONS. The data at VALADDR is in target byte order.
If the data are a string pointer, returns the number of string characters
printed. */
int
java_val_print (struct type *type, const gdb_byte *valaddr,
int embedded_offset, CORE_ADDR address,
struct ui_file *stream, int recurse,
const struct value_print_options *options)
{
unsigned int i = 0; /* Number of characters printed */
struct type *target_type;
CORE_ADDR addr;
CHECK_TYPEDEF (type);
switch (TYPE_CODE (type))
{
case TYPE_CODE_PTR:
if (options->format && options->format != 's')
{
print_scalar_formatted (valaddr, type, options, 0, stream);
break;
}
#if 0
if (options->vtblprint && cp_is_vtbl_ptr_type (type))
{
/* Print the unmangled name if desired. */
/* Print vtable entry - we only get here if we ARE using
-fvtable_thunks. (Otherwise, look under TYPE_CODE_STRUCT.) */
/* Extract an address, assume that it is unsigned. */
print_address_demangle (extract_unsigned_integer (valaddr, TYPE_LENGTH (type)),
stream, demangle);
break;
}
#endif
addr = unpack_pointer (type, valaddr);
if (addr == 0)
{
fputs_filtered ("null", stream);
return i;
}
target_type = check_typedef (TYPE_TARGET_TYPE (type));
if (TYPE_CODE (target_type) == TYPE_CODE_FUNC)
{
/* Try to print what function it points to. */
print_address_demangle (addr, stream, demangle);
/* Return value is irrelevant except for string pointers. */
return (0);
}
if (options->addressprint && options->format != 's')
{
fputs_filtered ("@", stream);
print_longest (stream, 'x', 0, (ULONGEST) addr);
}
return i;
case TYPE_CODE_CHAR:
case TYPE_CODE_INT:
/* Can't just call c_val_print because that prints bytes as C
chars. */
if (options->format || options->output_format)
{
struct value_print_options opts = *options;
opts.format = (options->format ? options->format
: options->output_format);
print_scalar_formatted (valaddr, type, &opts, 0, stream);
}
else if (TYPE_CODE (type) == TYPE_CODE_CHAR
|| (TYPE_CODE (type) == TYPE_CODE_INT
&& TYPE_LENGTH (type) == 2
&& strcmp (TYPE_NAME (type), "char") == 0))
LA_PRINT_CHAR ((int) unpack_long (type, valaddr), stream);
else
val_print_type_code_int (type, valaddr, stream);
break;
case TYPE_CODE_STRUCT:
java_print_value_fields (type, valaddr, address, stream, recurse,
options);
break;
default:
return c_val_print (type, valaddr, embedded_offset, address, stream,
recurse, options);
}
return 0;
}