re PR fortran/23815 (Add -byteswapio flag)
2005-02-06 Thomas Koenig <Thomas.Koenig@online.de> PR libfortran/23815 * gfortran.texi: Document the GFORTRAN_CONVERT_UNIT environment variable. * invoke.texi: Mention the "Runtime" chapter. Document the -fconvert= option. * gfortran.h: Add options_convert. * lang.opt: Add fconvert=little-endian, fconvert=big-endian, fconvert=native and fconvert=swap. * trans-decl.c (top level): Add gfor_fndecl_set_convert. (gfc_build_builtin_function_decls): Set gfor_fndecl_set_convert. (gfc_generate_function_code): If -fconvert was specified, and this is the main program, add a call to set_convert(). * options.c: Handle the -fconvert options. 2005-02-06 Thomas Koenig <Thomas.Koenig@online.de> PR libfortran/23815 * runtime/environ.c (init_unformatted): Add GFORTRAN_CONVERT_UNIT environment variable. (top level): Add defines, type and static variables for GFORTRAN_CONVERT_UNIT handling. (search_unit): New function. (match_word): New function. (match_integer): New function. (next_token): New function. (push_token): New function. (mark_single): New function. (mark_range): New funciton. (do_parse): New function. (init_unformatted): New function. (get_unformatted_convert): New function. * runtime/compile_options.c: Add set_convert(). * libgfortran.h: Add convert to compile_options_t. * io/open.c (st_open): Call get_unformatted_convert to get unit default; if CONVERT_NONE is returned, check for the presence of a CONVERT specifier and use it. As default, use compile_options.convert. * io/io.h (top level): Add CONVERT_NONE to unit_convert, to signal "nothing has been set". (top level): Add prototype for get_unformatted_convert. 2005-02-06 Thomas Koenig <Thomas.Koenig@online.de> PR libfortran/23815 * unf_io_convert_4.f90: New test. From-SVN: r110664
This commit is contained in:
parent
431ef78e8e
commit
eaa90d25da
15 changed files with 734 additions and 37 deletions
|
@ -1,3 +1,19 @@
|
|||
2005-02-06 Thomas Koenig <Thomas.Koenig@online.de>
|
||||
|
||||
PR libfortran/23815
|
||||
* gfortran.texi: Document the GFORTRAN_CONVERT_UNIT environment
|
||||
variable.
|
||||
* invoke.texi: Mention the "Runtime" chapter.
|
||||
Document the -fconvert= option.
|
||||
* gfortran.h: Add options_convert.
|
||||
* lang.opt: Add fconvert=little-endian, fconvert=big-endian,
|
||||
fconvert=native and fconvert=swap.
|
||||
* trans-decl.c (top level): Add gfor_fndecl_set_convert.
|
||||
(gfc_build_builtin_function_decls): Set gfor_fndecl_set_convert.
|
||||
(gfc_generate_function_code): If -fconvert was specified,
|
||||
and this is the main program, add a call to set_convert().
|
||||
* options.c: Handle the -fconvert options.
|
||||
|
||||
2006-02-06 Roger Sayle <roger@eyesopen.com>
|
||||
|
||||
* trans-stmt.c (gfc_evaluate_where_mask): Allow the NMASK argument
|
||||
|
|
|
@ -111,6 +111,12 @@ mstring;
|
|||
#define GFC_FPE_UNDERFLOW (1<<4)
|
||||
#define GFC_FPE_PRECISION (1<<5)
|
||||
|
||||
/* Keep this in sync with libgfortran/io/io.h ! */
|
||||
|
||||
typedef enum
|
||||
{ CONVERT_NATIVE=0, CONVERT_SWAP, CONVERT_BIG, CONVERT_LITTLE }
|
||||
options_convert;
|
||||
|
||||
|
||||
/*************************** Enums *****************************/
|
||||
|
||||
|
@ -1531,6 +1537,7 @@ typedef struct
|
|||
int allow_std;
|
||||
int warn_nonstd_intrinsics;
|
||||
int fshort_enums;
|
||||
int convert;
|
||||
}
|
||||
gfc_option_t;
|
||||
|
||||
|
|
|
@ -125,6 +125,7 @@ not accurately reflect the status of the most recent @command{gfortran}.
|
|||
* Project Status:: Status of @command{gfortran}, roadmap, proposed extensions.
|
||||
* Contributing:: How you can help.
|
||||
* Standards:: Standards supported by @command{gfortran}
|
||||
* Runtime:: Influencing runtime behavior with environment variables.
|
||||
* Extensions:: Language extensions implemented by @command{gfortran}
|
||||
* Intrinsic Procedures:: Intrinsic procedures supported by @command{gfortran}
|
||||
* Copying:: GNU General Public License says
|
||||
|
@ -545,13 +546,82 @@ Environment variable for temporary file directory.
|
|||
@item
|
||||
Environment variable forcing standard output to be line buffered (unix).
|
||||
|
||||
@item
|
||||
Variable for swapping endianness during unformatted read.
|
||||
|
||||
@item
|
||||
Variable for swapping Endianness during unformatted write.
|
||||
@end itemize
|
||||
|
||||
@node Runtime
|
||||
@chapter Runtime: Influencing runtime behavior with environment variables
|
||||
@cindex Runtime
|
||||
|
||||
The behaviour of the @command{gfortran} can be influenced by
|
||||
environment variables.
|
||||
@menu
|
||||
* GFORTRAN_CONVERT_UNIT:: Set endianness for unformatted I/O
|
||||
@end menu
|
||||
|
||||
@node GFORTRAN_CONVERT_UNIT
|
||||
@section GFORTRAN_CONVERT_UNIT --- Set endianness for unformatted I/O
|
||||
|
||||
By setting the @code{GFORTRAN_CONVERT_UNIT variable}, it is possible
|
||||
to change the representation of data for unformatted files.
|
||||
The syntax for the @code{GFORTRAN_CONVERT_UNIT} variable is:
|
||||
@smallexample
|
||||
GFORTRAN_CONVERT_UNIT: mode | mode ';' exception ;
|
||||
mode: 'native' | 'swap' | 'big_endian' | 'little_endian' ;
|
||||
exception: mode ':' unit_list | unit_list ;
|
||||
unit_list: unit_spec | unit_list unit_spec ;
|
||||
unit_spec: INTEGER | INTEGER '-' INTEGER ;
|
||||
@end smallexample
|
||||
The variable consists of an optional default mode, followed by
|
||||
a list of optional exceptions, which are separated by semicolons
|
||||
from the preceding default and each other. Each exception consists
|
||||
of a format and a comma-separated list of units. Valid values for
|
||||
the modes are the same as for the @code{CONVERT} specifier:
|
||||
|
||||
@itemize @w{}
|
||||
@item @code{NATIVE} Use the native format. This is the default.
|
||||
@item @code{SWAP} Swap between little- and big-endian.
|
||||
@item @code{LITTLE_ENDIAN} Use the little-endian format
|
||||
for unformatted files.
|
||||
@item @code{BIG_ENDIAN} Use the big-endian format for unformatted files.
|
||||
@end itemize
|
||||
A missing mode for an exception is taken to mean @code{BIG_ENDIAN}.
|
||||
Examples of values for @code{GFORTRAN_CONVERT_UNIT} are:
|
||||
@itemize @w{}
|
||||
@item @code{'big_endian'} Do all unformatted I/O in big_endian mode.
|
||||
@item @code{'little_endian;native:10-20,25'} Do all unformatted I/O
|
||||
in little_endian mode, except for units 10 to 20 and 25, which are in
|
||||
native format.
|
||||
@item @code{'10-20'} Units 10 to 20 are big-endian, the rest is native.
|
||||
@end itemize
|
||||
|
||||
Setting the environment variables should be done on the command
|
||||
line or via the @code{export}
|
||||
command for @code{sh}-compatible shells and via @code{setenv}
|
||||
for @code{csh}-compatible shells.
|
||||
|
||||
Example for @code{sh}:
|
||||
@smallexample
|
||||
$ gfortran foo.f90
|
||||
$ GFORTRAN_CONVERT_UNIT='big_endian;native:10-20' ./a.out
|
||||
@end smallexample
|
||||
|
||||
Example code for @code{csh}:
|
||||
@smallexample
|
||||
% gfortran foo.f90
|
||||
% setenv GFORTRAN_CONVERT_UNIT 'big_endian;native:10-20'
|
||||
% ./a.out
|
||||
@end smallexample
|
||||
|
||||
Using anything but the native representation for unformatted data
|
||||
carries a significant speed overhead. If speed in this area matters
|
||||
to you, it is best if you use this only for data that needs to be
|
||||
portable.
|
||||
|
||||
@xref{CONVERT specifier}, for an alternative way to specify the
|
||||
data representation for unformatted files. @xref{Runtime Options}, for
|
||||
setting a default data representation for the whole program. The
|
||||
@code{CONVERT} specifier overrides the @code{-fconvert} compile options.
|
||||
|
||||
@c ---------------------------------------------------------------------
|
||||
@c Extensions
|
||||
@c ---------------------------------------------------------------------
|
||||
|
@ -937,16 +1007,18 @@ will not change the base address of the array that was passed.
|
|||
|
||||
gfortran allows the conversion of unformatted data between little-
|
||||
and big-endian representation to facilitate moving of data
|
||||
between different systems. The conversion is indicated with
|
||||
between different systems. The conversion can be indicated with
|
||||
the @code{CONVERT} specifier on the @code{OPEN} statement.
|
||||
@xref{GFORTRAN_CONVERT_UNIT}, for an alternative way of specifying
|
||||
the data format via an environment variable.
|
||||
|
||||
Valid values for @code{CONVERT} are:
|
||||
@itemize @w{}
|
||||
@item @code{CONVERT='NATIVE'} Use the native format. This is the default.
|
||||
@item @code{CONVERT='SWAP'} Swap between little- and big-endian.
|
||||
@item @code{CONVERT='LITTLE_ENDIAN'} Use the little-endian format
|
||||
@item @code{CONVERT='LITTLE_ENDIAN'} Use the little-endian representation
|
||||
for unformatted files.
|
||||
@item @code{CONVERT='BIG_ENDIAN'} Use the big-endian format for
|
||||
@item @code{CONVERT='BIG_ENDIAN'} Use the big-endian representation for
|
||||
unformatted files.
|
||||
@end itemize
|
||||
|
||||
|
@ -967,6 +1039,16 @@ on IEEE systems of kinds 4 and 8. Conversion between different
|
|||
m68k and x86_64, which gfortran
|
||||
supports as @code{REAL(KIND=10)} will probably not work.
|
||||
|
||||
@emph{Note that the values specified via the GFORTRAN_CONVERT_UNIT
|
||||
environment variable will override the CONVERT specifier in the
|
||||
open statement}. This is to give control over data formats to
|
||||
a user who does not have the source code of his program available.
|
||||
|
||||
Using anything but the native representation for unformatted data
|
||||
carries a significant speed overhead. If speed in this area matters
|
||||
to you, it is best if you use this only for data that needs to be
|
||||
portable.
|
||||
|
||||
@c ---------------------------------------------------------------------
|
||||
@include intrinsic.texi
|
||||
@c ---------------------------------------------------------------------
|
||||
|
|
|
@ -98,6 +98,7 @@ one is not the default.
|
|||
* Warning Options:: How picky should the compiler be?
|
||||
* Debugging Options:: Symbol tables, measurements, and debugging dumps.
|
||||
* Directory Options:: Where to find module files
|
||||
* Runtime Options:: Influencing runtime behavior
|
||||
* Code Gen Options:: Specifying conventions for function calls, data layout
|
||||
and register usage.
|
||||
* Environment Variables:: Env vars that affect GNU Fortran.
|
||||
|
@ -141,6 +142,11 @@ by type. Explanations are in the following sections.
|
|||
@gccoptlist{
|
||||
-I@var{dir} -M@var{dir}}
|
||||
|
||||
@item Runtime Options
|
||||
@xref{Runtime Options,,Options for influencing runtime behavior}.
|
||||
@gccoptlist{
|
||||
-fconvert=@var{conversion}}
|
||||
|
||||
@item Code Generation Options
|
||||
@xref{Code Gen Options,,Options for Code Generation Conventions}.
|
||||
@gccoptlist{
|
||||
|
@ -155,6 +161,7 @@ by type. Explanations are in the following sections.
|
|||
* Warning Options:: How picky should the compiler be?
|
||||
* Debugging Options:: Symbol tables, measurements, and debugging dumps.
|
||||
* Directory Options:: Where to find module files
|
||||
* Runtime Options:: Influencing runtime behavior
|
||||
* Code Gen Options:: Specifying conventions for function calls, data layout
|
||||
and register usage.
|
||||
@end menu
|
||||
|
@ -557,6 +564,25 @@ The default is the current directory.
|
|||
GCC options.
|
||||
@end table
|
||||
|
||||
@node Runtime Options
|
||||
@section Influencing runtime behavior
|
||||
@cindex runtime, options
|
||||
|
||||
These options affect the runtime behavior of @command{gfortran}.
|
||||
@table @gcctabopt
|
||||
@cindex -fconvert=@var{conversion} option
|
||||
@item -fconvert=@var{conversion}
|
||||
Specify the representation of data for unformatted files. Valid
|
||||
values for conversion are: @samp{native}, the default; @samp{swap},
|
||||
swap between big- and little-endian; @samp{big-endian}, use big-endian
|
||||
representation for unformatted files; @samp{little-endian}, use little-endian
|
||||
representation for unformatted files.
|
||||
|
||||
@emph{This option has an effect only when used in the main program.
|
||||
The @code{CONVERT} specifier and the GFORTRAN_CONVERT_UNIT environment
|
||||
variable override the default specified by -fconvert.}
|
||||
@end table
|
||||
|
||||
@node Code Gen Options
|
||||
@section Options for Code Generation Conventions
|
||||
@cindex code generation, conventions
|
||||
|
@ -796,4 +822,6 @@ that affect the operation of @command{gcc}.
|
|||
gcc,Using the GNU Compiler Collection (GCC)}, for information on environment
|
||||
variables.
|
||||
|
||||
@xref{Runtime}, for environment variables that affect the
|
||||
run-time behavior of @command{gfortran} programs.
|
||||
@c man end
|
||||
|
|
|
@ -205,4 +205,20 @@ fshort-enums
|
|||
Fortran
|
||||
Use the narrowest integer type possible for enumeration types
|
||||
|
||||
fconvert=little-endian
|
||||
Fortran RejectNegative
|
||||
Use little-endian format for unformatted files
|
||||
|
||||
fconvert=big-endian
|
||||
Fortran RejectNegative
|
||||
Use big-endian format for unformatted files
|
||||
|
||||
fconvert=native
|
||||
Fortran RejectNegative
|
||||
Use native format for unformatted files
|
||||
|
||||
fconvert=swap
|
||||
Fortran RejectNegative
|
||||
Swap endianness for unformatted files
|
||||
|
||||
; This comment is to ensure we retain the blank line above.
|
||||
|
|
|
@ -573,6 +573,22 @@ gfc_handle_option (size_t scode, const char *arg, int value)
|
|||
case OPT_fshort_enums:
|
||||
gfc_option.fshort_enums = 1;
|
||||
break;
|
||||
|
||||
case OPT_fconvert_little_endian:
|
||||
gfc_option.convert = CONVERT_LITTLE;
|
||||
break;
|
||||
|
||||
case OPT_fconvert_big_endian:
|
||||
gfc_option.convert = CONVERT_BIG;
|
||||
break;
|
||||
|
||||
case OPT_fconvert_native:
|
||||
gfc_option.convert = CONVERT_NATIVE;
|
||||
break;
|
||||
|
||||
case OPT_fconvert_swap:
|
||||
gfc_option.convert = CONVERT_SWAP;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -88,6 +88,7 @@ tree gfor_fndecl_select_string;
|
|||
tree gfor_fndecl_runtime_error;
|
||||
tree gfor_fndecl_set_fpe;
|
||||
tree gfor_fndecl_set_std;
|
||||
tree gfor_fndecl_set_convert;
|
||||
tree gfor_fndecl_ctime;
|
||||
tree gfor_fndecl_fdate;
|
||||
tree gfor_fndecl_ttynam;
|
||||
|
@ -2229,6 +2230,10 @@ gfc_build_builtin_function_decls (void)
|
|||
gfc_int4_type_node,
|
||||
gfc_int4_type_node);
|
||||
|
||||
gfor_fndecl_set_convert =
|
||||
gfc_build_library_function_decl (get_identifier (PREFIX("set_convert")),
|
||||
void_type_node, 1, gfc_c_int_type_node);
|
||||
|
||||
gfor_fndecl_in_pack = gfc_build_library_function_decl (
|
||||
get_identifier (PREFIX("internal_pack")),
|
||||
pvoid_type_node, 1, pvoid_type_node);
|
||||
|
@ -2845,6 +2850,22 @@ gfc_generate_function_code (gfc_namespace * ns)
|
|||
gfc_add_expr_to_block (&body, tmp);
|
||||
}
|
||||
|
||||
/* If this is the main program and an -fconvert option was provided,
|
||||
add a call to set_convert. */
|
||||
|
||||
if (sym->attr.is_main_program && gfc_option.convert != CONVERT_NATIVE)
|
||||
{
|
||||
tree arglist, gfc_c_int_type_node;
|
||||
|
||||
gfc_c_int_type_node = gfc_get_int_type (gfc_c_int_kind);
|
||||
arglist = gfc_chainon_list (NULL_TREE,
|
||||
build_int_cst (gfc_c_int_type_node,
|
||||
gfc_option.convert));
|
||||
tmp = build_function_call_expr (gfor_fndecl_set_convert, arglist);
|
||||
gfc_add_expr_to_block (&body, tmp);
|
||||
}
|
||||
|
||||
|
||||
if (TREE_TYPE (DECL_RESULT (fndecl)) != void_type_node
|
||||
&& sym->attr.subroutine)
|
||||
{
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
2005-02-06 Thomas Koenig <Thomas.Koenig@online.de>
|
||||
|
||||
PR libfortran/23815
|
||||
* unf_io_convert_4.f90: New test.
|
||||
|
||||
2006-02-06 Daniel Berlin <dberlin@dberlin.org>
|
||||
|
||||
* gcc.dg/tree-ssa/loadpre10.c: New test.
|
||||
|
|
14
gcc/testsuite/gfortran.dg/unf_io_convert_4.f90
Normal file
14
gcc/testsuite/gfortran.dg/unf_io_convert_4.f90
Normal file
|
@ -0,0 +1,14 @@
|
|||
! { dg-do run }
|
||||
! { dg-options "-fconvert=big-endian" }
|
||||
program main
|
||||
character (len=30) ch
|
||||
open (10,form="unformatted",convert="little_endian")
|
||||
inquire (10, convert=ch)
|
||||
if (ch .ne. "LITTLE_ENDIAN") call abort
|
||||
close (10, status="delete")
|
||||
|
||||
open(11,form="unformatted")
|
||||
inquire (11, convert=ch)
|
||||
if (ch .ne. "BIG_ENDIAN") call abort
|
||||
close (11, status="delete")
|
||||
end program main
|
|
@ -1,3 +1,30 @@
|
|||
2005-02-06 Thomas Koenig <Thomas.Koenig@online.de>
|
||||
|
||||
PR libfortran/23815
|
||||
* runtime/environ.c (init_unformatted): Add GFORTRAN_CONVERT_UNIT
|
||||
environment variable.
|
||||
(top level): Add defines, type and static variables for
|
||||
GFORTRAN_CONVERT_UNIT handling.
|
||||
(search_unit): New function.
|
||||
(match_word): New function.
|
||||
(match_integer): New function.
|
||||
(next_token): New function.
|
||||
(push_token): New function.
|
||||
(mark_single): New function.
|
||||
(mark_range): New funciton.
|
||||
(do_parse): New function.
|
||||
(init_unformatted): New function.
|
||||
(get_unformatted_convert): New function.
|
||||
* runtime/compile_options.c: Add set_convert().
|
||||
* libgfortran.h: Add convert to compile_options_t.
|
||||
* io/open.c (st_open): Call get_unformatted_convert to get
|
||||
unit default; if CONVERT_NONE is returned, check for
|
||||
the presence of a CONVERT specifier and use it.
|
||||
As default, use compile_options.convert.
|
||||
* io/io.h (top level): Add CONVERT_NONE to unit_convert, to signal
|
||||
"nothing has been set".
|
||||
(top level): Add prototype for get_unformatted_convert.
|
||||
|
||||
2006-02-06 Francois-Xavier Coudert <coudert@clipper.ens.fr>
|
||||
|
||||
PR libfortran/24685
|
||||
|
|
|
@ -207,7 +207,7 @@ typedef enum
|
|||
unit_mode;
|
||||
|
||||
typedef enum
|
||||
{ CONVERT_NATIVE, CONVERT_SWAP, CONVERT_BIG, CONVERT_LITTLE }
|
||||
{ CONVERT_NONE=-1, CONVERT_NATIVE, CONVERT_SWAP, CONVERT_BIG, CONVERT_LITTLE }
|
||||
unit_convert;
|
||||
|
||||
#define CHARACTER1(name) \
|
||||
|
@ -884,3 +884,8 @@ dec_waiting_unlocked (gfc_unit *u)
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* ../runtime/environ.c This is here because we return unit_convert. */
|
||||
|
||||
unit_convert get_unformatted_convert (int);
|
||||
internal_proto(get_unformatted_convert);
|
||||
|
|
|
@ -502,6 +502,7 @@ st_open (st_parameter_open *opp)
|
|||
unit_flags flags;
|
||||
gfc_unit *u = NULL;
|
||||
GFC_INTEGER_4 cf = opp->common.flags;
|
||||
unit_convert conv;
|
||||
|
||||
library_start (&opp->common);
|
||||
|
||||
|
@ -539,35 +540,44 @@ st_open (st_parameter_open *opp)
|
|||
find_option (&opp->common, opp->status, opp->status_len,
|
||||
status_opt, "Bad STATUS parameter in OPEN statement");
|
||||
|
||||
if (cf & IOPARM_OPEN_HAS_CONVERT)
|
||||
/* First, we check wether the convert flag has been set via environment
|
||||
variable. This overrides the convert tag in the open statement. */
|
||||
|
||||
conv = get_unformatted_convert (opp->common.unit);
|
||||
|
||||
if (conv == CONVERT_NONE)
|
||||
{
|
||||
unit_convert conv;
|
||||
conv = find_option (&opp->common, opp->convert, opp->convert_len,
|
||||
convert_opt, "Bad CONVERT parameter in OPEN statement");
|
||||
/* We use l8_to_l4_offset, which is 0 on little-endian machines
|
||||
and 1 on big-endian machines. */
|
||||
switch (conv)
|
||||
{
|
||||
case CONVERT_NATIVE:
|
||||
case CONVERT_SWAP:
|
||||
break;
|
||||
|
||||
case CONVERT_BIG:
|
||||
conv = l8_to_l4_offset ? CONVERT_NATIVE : CONVERT_SWAP;
|
||||
break;
|
||||
|
||||
case CONVERT_LITTLE:
|
||||
conv = l8_to_l4_offset ? CONVERT_SWAP : CONVERT_NATIVE;
|
||||
break;
|
||||
|
||||
default:
|
||||
internal_error (&opp->common, "Illegal value for CONVERT");
|
||||
break;
|
||||
}
|
||||
flags.convert = conv;
|
||||
/* Nothing has been set by environment variable, check the convert tag. */
|
||||
if (cf & IOPARM_OPEN_HAS_CONVERT)
|
||||
conv = find_option (&opp->common, opp->convert, opp->convert_len,
|
||||
convert_opt,
|
||||
"Bad CONVERT parameter in OPEN statement");
|
||||
else
|
||||
conv = compile_options.convert;
|
||||
}
|
||||
else
|
||||
flags.convert = CONVERT_NATIVE;
|
||||
|
||||
/* We use l8_to_l4_offset, which is 0 on little-endian machines
|
||||
and 1 on big-endian machines. */
|
||||
switch (conv)
|
||||
{
|
||||
case CONVERT_NATIVE:
|
||||
case CONVERT_SWAP:
|
||||
break;
|
||||
|
||||
case CONVERT_BIG:
|
||||
conv = l8_to_l4_offset ? CONVERT_NATIVE : CONVERT_SWAP;
|
||||
break;
|
||||
|
||||
case CONVERT_LITTLE:
|
||||
conv = l8_to_l4_offset ? CONVERT_SWAP : CONVERT_NATIVE;
|
||||
break;
|
||||
|
||||
default:
|
||||
internal_error (&opp->common, "Illegal value for CONVERT");
|
||||
break;
|
||||
}
|
||||
|
||||
flags.convert = conv;
|
||||
|
||||
if (opp->common.unit < 0)
|
||||
generate_error (&opp->common, ERROR_BAD_OPTION,
|
||||
|
|
|
@ -336,6 +336,7 @@ typedef struct
|
|||
{
|
||||
int warn_std;
|
||||
int allow_std;
|
||||
int convert;
|
||||
}
|
||||
compile_options_t;
|
||||
|
||||
|
|
|
@ -59,3 +59,15 @@ init_compile_options (void)
|
|||
compile_options.allow_std = GFC_STD_F95_OBS | GFC_STD_F95_DEL
|
||||
| GFC_STD_F2003 | GFC_STD_F95 | GFC_STD_F77 | GFC_STD_GNU | GFC_STD_LEGACY;
|
||||
}
|
||||
|
||||
/* Function called by the front-end to tell us the
|
||||
default for unformatted data conversion. */
|
||||
|
||||
extern void set_convert (int);
|
||||
export_proto (set_convert);
|
||||
|
||||
void
|
||||
set_convert (int conv)
|
||||
{
|
||||
compile_options.convert = conv;
|
||||
}
|
||||
|
|
|
@ -61,8 +61,9 @@ typedef struct variable
|
|||
}
|
||||
variable;
|
||||
|
||||
static void init_unformatted (variable *);
|
||||
|
||||
/* print_spaces()-- Print a particular number of spaces */
|
||||
/* print_spaces()-- Print a particular number of spaces. */
|
||||
|
||||
static void
|
||||
print_spaces (int n)
|
||||
|
@ -533,6 +534,11 @@ static variable variable_table[] = {
|
|||
show_precision,
|
||||
"Precision of intermediate results. Values are 24, 53 and 64.", 0},
|
||||
|
||||
/* GFORTRAN_CONVERT_UNIT - Set the default data conversion for
|
||||
unformatted I/O. */
|
||||
{"GFORTRAN_CONVERT_UNIT", 0, 0, init_unformatted, show_string,
|
||||
"Set format for unformatted files", 0},
|
||||
|
||||
{NULL, 0, NULL, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
|
@ -623,3 +629,434 @@ show_variables (void)
|
|||
|
||||
sys_exit (0);
|
||||
}
|
||||
|
||||
/* This is the handling of the GFORTRAN_CONVERT_UNITS environment variable.
|
||||
It is called from environ.c to parse this variable, and from
|
||||
open.c to determine if the user specified a default for an
|
||||
unformatted file.
|
||||
The syntax of the environment variable is, in bison grammar:
|
||||
|
||||
GFORTRAN_CONVERT_UNITS: mode | mode ';' exception ;
|
||||
mode: 'native' | 'swap' | 'big_endian' | 'little_endian' ;
|
||||
exception: mode ':' unit_list | unit_list ;
|
||||
unit_list: unit_spec | unit_list unit_spec ;
|
||||
unit_spec: INTEGER | INTEGER '-' INTEGER ;
|
||||
*/
|
||||
|
||||
/* Defines for the tokens. Other valid tokens are ',', ':', '-'. */
|
||||
|
||||
|
||||
#define NATIVE 257
|
||||
#define SWAP 258
|
||||
#define BIG 259
|
||||
#define LITTLE 260
|
||||
/* Some space for additional tokens later. */
|
||||
#define INTEGER 273
|
||||
#define END (-1)
|
||||
#define ILLEGAL (-2)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int unit;
|
||||
unit_convert conv;
|
||||
} exception_t;
|
||||
|
||||
|
||||
static char *p; /* Main character pointer for parsing. */
|
||||
static char *lastpos; /* Auxiliary pointer, for backing up. */
|
||||
static int unit_num; /* The last unit number read. */
|
||||
static int unit_count; /* The number of units found. */
|
||||
static int do_count; /* Parsing is done twice - first to count the number
|
||||
of units, then to fill in the table. This
|
||||
variable controls what to do. */
|
||||
static exception_t *elist; /* The list of exceptions to the default. This is
|
||||
sorted according to unit number. */
|
||||
static int n_elist; /* Number of exceptions to the default. */
|
||||
|
||||
static unit_convert endian; /* Current endianness. */
|
||||
|
||||
static unit_convert def; /* Default as specified (if any). */
|
||||
|
||||
/* Search for a unit number, using a binary search. The
|
||||
first argument is the unit number to search for. The second argument
|
||||
is a pointer to an index.
|
||||
If the unit number is found, the function returns 1, and the index
|
||||
is that of the element.
|
||||
If the unit number is not found, the function returns 0, and the
|
||||
index is the one where the element would be inserted. */
|
||||
|
||||
static int
|
||||
search_unit (int unit, int *ip)
|
||||
{
|
||||
int low, high, mid;
|
||||
|
||||
low = -1;
|
||||
high = n_elist;
|
||||
while (high - low > 1)
|
||||
{
|
||||
mid = (low + high) / 2;
|
||||
if (unit <= elist[mid].unit)
|
||||
high = mid;
|
||||
else
|
||||
low = mid;
|
||||
}
|
||||
*ip = high;
|
||||
if (elist[high].unit == unit)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This matches a keyword. If it is found, return the token supplied,
|
||||
otherwise return ILLEGAL. */
|
||||
|
||||
static int
|
||||
match_word (const char *word, int tok)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (strncasecmp (p, word, strlen (word)) == 0)
|
||||
{
|
||||
p += strlen (word);
|
||||
res = tok;
|
||||
}
|
||||
else
|
||||
res = ILLEGAL;
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
/* Match an integer and store its value in unit_num. This only works
|
||||
if p actually points to the start of an integer. The caller has
|
||||
to ensure this. */
|
||||
|
||||
static int
|
||||
match_integer (void)
|
||||
{
|
||||
unit_num = 0;
|
||||
while (isdigit (*p))
|
||||
unit_num = unit_num * 10 + (*p++ - '0');
|
||||
return INTEGER;
|
||||
|
||||
}
|
||||
|
||||
/* This reads the next token from the GFORTRAN_CONVERT_UNITS variable.
|
||||
Returned values are the different tokens. */
|
||||
|
||||
static int
|
||||
next_token (void)
|
||||
{
|
||||
int result;
|
||||
|
||||
lastpos = p;
|
||||
switch (*p)
|
||||
{
|
||||
case '\0':
|
||||
result = END;
|
||||
break;
|
||||
|
||||
case ':':
|
||||
case ',':
|
||||
case '-':
|
||||
case ';':
|
||||
result = *p;
|
||||
p++;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
case 'B':
|
||||
result = match_word ("big_endian", BIG);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
case 'L':
|
||||
result = match_word ("little_endian", LITTLE);
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
case 'N':
|
||||
result = match_word ("native", NATIVE);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
case 'S':
|
||||
result = match_word ("swap", SWAP);
|
||||
break;
|
||||
|
||||
case '1': case '2': case '3': case '4': case '5':
|
||||
case '6': case '7': case '8': case '9':
|
||||
result = match_integer ();
|
||||
break;
|
||||
|
||||
default:
|
||||
result = ILLEGAL;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Back up the last token by setting back the character pointer. */
|
||||
|
||||
static void
|
||||
push_token (void)
|
||||
{
|
||||
p = lastpos;
|
||||
}
|
||||
|
||||
/* This is called when a unit is identified. If do_count is nonzero,
|
||||
increment the number of units by one. If do_count is zero,
|
||||
put the unit into the table. */
|
||||
|
||||
static void
|
||||
mark_single (int unit)
|
||||
{
|
||||
int i,j;
|
||||
|
||||
if (do_count)
|
||||
{
|
||||
unit_count++;
|
||||
return;
|
||||
}
|
||||
if (search_unit (unit, &i))
|
||||
{
|
||||
elist[unit].conv = endian;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (j=n_elist; j>=i; j--)
|
||||
elist[j+1] = elist[j];
|
||||
|
||||
n_elist += 1;
|
||||
elist[i].unit = unit;
|
||||
elist[i].conv = endian;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is called when a unit range is identified. If do_count is
|
||||
nonzero, increase the number of units. If do_count is zero,
|
||||
put the unit into the table. */
|
||||
|
||||
static void
|
||||
mark_range (int unit1, int unit2)
|
||||
{
|
||||
int i;
|
||||
if (do_count)
|
||||
unit_count += abs (unit2 - unit1) + 1;
|
||||
else
|
||||
{
|
||||
if (unit2 < unit1)
|
||||
for (i=unit2; i<=unit1; i++)
|
||||
mark_single (i);
|
||||
else
|
||||
for (i=unit1; i<=unit2; i++)
|
||||
mark_single (i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse the GFORTRAN_CONVERT_UNITS variable. This is called
|
||||
twice, once to count the units and once to actually mark them in
|
||||
the table. When counting, we don't check for double occurences
|
||||
of units. */
|
||||
|
||||
static int
|
||||
do_parse (void)
|
||||
{
|
||||
int tok, def;
|
||||
int unit1;
|
||||
int continue_ulist;
|
||||
char *start;
|
||||
|
||||
unit_count = 0;
|
||||
|
||||
def = 0;
|
||||
start = p;
|
||||
|
||||
/* Parse the string. First, let's look for a default. */
|
||||
tok = next_token ();
|
||||
switch (tok)
|
||||
{
|
||||
case NATIVE:
|
||||
endian = CONVERT_NATIVE;
|
||||
break;
|
||||
|
||||
case SWAP:
|
||||
endian = CONVERT_SWAP;
|
||||
break;
|
||||
|
||||
case BIG:
|
||||
endian = CONVERT_BIG;
|
||||
break;
|
||||
|
||||
case LITTLE:
|
||||
endian = CONVERT_LITTLE;
|
||||
break;
|
||||
|
||||
case INTEGER:
|
||||
/* A leading digit means that we are looking at an exception.
|
||||
Reset the position to the beginning, and continue processing
|
||||
at the exception list. */
|
||||
p = start;
|
||||
goto exceptions;
|
||||
break;
|
||||
|
||||
case END:
|
||||
goto end;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto error;
|
||||
break;
|
||||
}
|
||||
|
||||
tok = next_token ();
|
||||
switch (tok)
|
||||
{
|
||||
case ';':
|
||||
def = endian;
|
||||
break;
|
||||
|
||||
case ':':
|
||||
/* This isn't a default after all. Reset the position to the
|
||||
beginning, and continue processing at the exception list. */
|
||||
p = start;
|
||||
goto exceptions;
|
||||
break;
|
||||
|
||||
case END:
|
||||
goto end;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto error;
|
||||
break;
|
||||
}
|
||||
|
||||
exceptions:
|
||||
|
||||
/* Loop over all exceptions. */
|
||||
while(1)
|
||||
{
|
||||
tok = next_token ();
|
||||
switch (tok)
|
||||
{
|
||||
case LITTLE:
|
||||
if (next_token () != ':')
|
||||
goto error;
|
||||
endian = CONVERT_LITTLE;
|
||||
break;
|
||||
|
||||
case BIG:
|
||||
if (next_token () != ':')
|
||||
goto error;
|
||||
endian = CONVERT_BIG;
|
||||
break;
|
||||
|
||||
case INTEGER:
|
||||
push_token ();
|
||||
break;
|
||||
|
||||
case END:
|
||||
goto end;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto error;
|
||||
break;
|
||||
}
|
||||
/* We arrive here when we want to parse a list of
|
||||
numbers. */
|
||||
continue_ulist = 1;
|
||||
do
|
||||
{
|
||||
tok = next_token ();
|
||||
if (tok != INTEGER)
|
||||
goto error;
|
||||
|
||||
unit1 = unit_num;
|
||||
tok = next_token ();
|
||||
/* The number can be followed by a - and another number,
|
||||
which means that this is a unit range, a comma
|
||||
or a semicolon. */
|
||||
if (tok == '-')
|
||||
{
|
||||
if (next_token () != INTEGER)
|
||||
goto error;
|
||||
|
||||
mark_range (unit1, unit_num);
|
||||
tok = next_token ();
|
||||
if (tok == END)
|
||||
goto end;
|
||||
else if (tok == ';')
|
||||
continue_ulist = 0;
|
||||
else if (tok != ',')
|
||||
goto error;
|
||||
}
|
||||
else
|
||||
{
|
||||
mark_single (unit1);
|
||||
switch (tok)
|
||||
{
|
||||
case ';':
|
||||
continue_ulist = 0;
|
||||
break;
|
||||
|
||||
case ',':
|
||||
break;
|
||||
|
||||
case END:
|
||||
goto end;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
} while (continue_ulist);
|
||||
}
|
||||
end:
|
||||
return 0;
|
||||
error:
|
||||
def = CONVERT_NONE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void init_unformatted (variable * v)
|
||||
{
|
||||
char *val;
|
||||
val = getenv (v->name);
|
||||
def = CONVERT_NONE;
|
||||
n_elist = 0;
|
||||
|
||||
if (val == NULL)
|
||||
return;
|
||||
do_count = 1;
|
||||
p = val;
|
||||
do_parse ();
|
||||
if (do_count <= 0)
|
||||
{
|
||||
n_elist = 0;
|
||||
elist = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
elist = get_mem (unit_count * sizeof (exception_t));
|
||||
do_count = 0;
|
||||
p = val;
|
||||
do_parse ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the default conversion for for an unformatted unit. */
|
||||
|
||||
unit_convert
|
||||
get_unformatted_convert (int unit)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (elist == NULL)
|
||||
return def;
|
||||
else if (search_unit (unit, &i))
|
||||
return elist[i].conv;
|
||||
else
|
||||
return def;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue