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.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@110664
138bc75d-0d04-0410-961f-
82ee72b054a4
+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
#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 *****************************/
int allow_std;
int warn_nonstd_intrinsics;
int fshort_enums;
+ int convert;
}
gfc_option_t;
* 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
@item
Environment variable forcing standard output to be line buffered (unix).
-@item
-Variable for swapping endianness during unformatted read.
+@end itemize
-@item
-Variable for swapping Endianness during unformatted write.
+@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 ---------------------------------------------------------------------
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
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 ---------------------------------------------------------------------
* 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.
@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{
* 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
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
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
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.
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;
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;
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);
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)
{
+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.
--- /dev/null
+! { 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
+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
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) \
}
#endif
+
+/* ../runtime/environ.c This is here because we return unit_convert. */
+
+unit_convert get_unformatted_convert (int);
+internal_proto(get_unformatted_convert);
unit_flags flags;
gfc_unit *u = NULL;
GFC_INTEGER_4 cf = opp->common.flags;
+ unit_convert conv;
library_start (&opp->common);
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,
{
int warn_std;
int allow_std;
+ int convert;
}
compile_options_t;
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;
+}
}
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)
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}
};
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;
+}