diff options
-rw-r--r-- | gcc/fortran/ChangeLog | 16 | ||||
-rw-r--r-- | gcc/fortran/gfortran.h | 7 | ||||
-rw-r--r-- | gcc/fortran/gfortran.texi | 96 | ||||
-rw-r--r-- | gcc/fortran/invoke.texi | 28 | ||||
-rw-r--r-- | gcc/fortran/lang.opt | 16 | ||||
-rw-r--r-- | gcc/fortran/options.c | 16 | ||||
-rw-r--r-- | gcc/fortran/trans-decl.c | 21 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 5 | ||||
-rw-r--r-- | gcc/testsuite/gfortran.dg/unf_io_convert_4.f90 | 14 | ||||
-rw-r--r-- | libgfortran/ChangeLog | 27 | ||||
-rw-r--r-- | libgfortran/io/io.h | 7 | ||||
-rw-r--r-- | libgfortran/io/open.c | 64 | ||||
-rw-r--r-- | libgfortran/libgfortran.h | 1 | ||||
-rw-r--r-- | libgfortran/runtime/compile_options.c | 12 | ||||
-rw-r--r-- | libgfortran/runtime/environ.c | 439 |
15 files changed, 733 insertions, 36 deletions
diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index dbd94d52427..66f638f6301 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -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 diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h index a1aaaf09967..31d5a4eca0e 100644 --- a/gcc/fortran/gfortran.h +++ b/gcc/fortran/gfortran.h @@ -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; diff --git a/gcc/fortran/gfortran.texi b/gcc/fortran/gfortran.texi index b4f1bf95c22..65a2542de6e 100644 --- a/gcc/fortran/gfortran.texi +++ b/gcc/fortran/gfortran.texi @@ -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. +@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 --------------------------------------------------------------------- @@ -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 --------------------------------------------------------------------- diff --git a/gcc/fortran/invoke.texi b/gcc/fortran/invoke.texi index 5816207d4a6..8d7a1d52f11 100644 --- a/gcc/fortran/invoke.texi +++ b/gcc/fortran/invoke.texi @@ -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 diff --git a/gcc/fortran/lang.opt b/gcc/fortran/lang.opt index 465d589813a..5ce2934f590 100644 --- a/gcc/fortran/lang.opt +++ b/gcc/fortran/lang.opt @@ -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. diff --git a/gcc/fortran/options.c b/gcc/fortran/options.c index d65827c9bb3..0b2f7b36f21 100644 --- a/gcc/fortran/options.c +++ b/gcc/fortran/options.c @@ -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; diff --git a/gcc/fortran/trans-decl.c b/gcc/fortran/trans-decl.c index cdbb9995567..4811b7a1d60 100644 --- a/gcc/fortran/trans-decl.c +++ b/gcc/fortran/trans-decl.c @@ -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) { diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 624e5985622..3262a6ab82e 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -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. diff --git a/gcc/testsuite/gfortran.dg/unf_io_convert_4.f90 b/gcc/testsuite/gfortran.dg/unf_io_convert_4.f90 new file mode 100644 index 00000000000..88cb78ff0b5 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/unf_io_convert_4.f90 @@ -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 diff --git a/libgfortran/ChangeLog b/libgfortran/ChangeLog index ef4db4f60be..4bcdb84667d 100644 --- a/libgfortran/ChangeLog +++ b/libgfortran/ChangeLog @@ -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 diff --git a/libgfortran/io/io.h b/libgfortran/io/io.h index e36417100cd..31b4927cb6e 100644 --- a/libgfortran/io/io.h +++ b/libgfortran/io/io.h @@ -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); diff --git a/libgfortran/io/open.c b/libgfortran/io/open.c index 3dc2b11955c..1459f8f310a 100644 --- a/libgfortran/io/open.c +++ b/libgfortran/io/open.c @@ -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, diff --git a/libgfortran/libgfortran.h b/libgfortran/libgfortran.h index 3b8eed26cb9..fac9b4ab328 100644 --- a/libgfortran/libgfortran.h +++ b/libgfortran/libgfortran.h @@ -336,6 +336,7 @@ typedef struct { int warn_std; int allow_std; + int convert; } compile_options_t; diff --git a/libgfortran/runtime/compile_options.c b/libgfortran/runtime/compile_options.c index fdc7b780cf7..e2a2ffa4c80 100644 --- a/libgfortran/runtime/compile_options.c +++ b/libgfortran/runtime/compile_options.c @@ -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; +} diff --git a/libgfortran/runtime/environ.c b/libgfortran/runtime/environ.c index 09743a0eb95..c519f084573 100644 --- a/libgfortran/runtime/environ.c +++ b/libgfortran/runtime/environ.c @@ -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; +} |