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:
Thomas Koenig 2006-02-06 20:12:44 +00:00 committed by Thomas Koenig
parent 431ef78e8e
commit eaa90d25da
15 changed files with 734 additions and 37 deletions

View file

@ -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

View file

@ -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;

View file

@ -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 ---------------------------------------------------------------------

View file

@ -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

View file

@ -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.

View file

@ -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;

View file

@ -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)
{

View file

@ -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.

View 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

View file

@ -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

View file

@ -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);

View file

@ -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,

View file

@ -336,6 +336,7 @@ typedef struct
{
int warn_std;
int allow_std;
int convert;
}
compile_options_t;

View file

@ -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;
}

View file

@ -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;
}