/* Copyright (C) 2002-2003 Free Software Foundation, Inc. Contributed by Andy Vaught This file is part of the GNU Fortran 95 runtime library (libgfor). Libgfor is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. Libgfor is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with libgfor; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include "libgfortran.h" #include "../io/io.h" /* Environment scanner. Examine the environment for controlling minor * aspects of the program's execution. Our philosophy here that the * environment should not prevent the program from running, so an * environment variable with a messed-up value will be interpreted in * the default way. * * Most of the environment is checked early in the startup sequence, * but other variables are checked during execution of the user's * program. */ options_t options; extern char **environ; typedef struct variable { const char *name; int value, *var; void (*init) (struct variable *); void (*show) (struct variable *); const char *desc; int bad; } variable; /* print_spaces()-- Print a particular number of spaces */ static void print_spaces (int n) { char buffer[80]; int i; if (n <= 0) return; for (i = 0; i < n; i++) buffer[i] = ' '; buffer[i] = '\0'; st_printf (buffer); } /* var_source()-- Return a string that describes where the value of a * variable comes from */ static const char * var_source (variable * v) { if (getenv (v->name) == NULL) return "Default"; if (v->bad) return "Bad "; return "Set "; } /* init_integer()-- Initialize an integer environment variable */ static void init_integer (variable * v) { char *p, *q; p = getenv (v->name); if (p == NULL) goto set_default; for (q = p; *q; q++) if (!isdigit (*q)) { v->bad = 1; goto set_default; } *v->var = atoi (p); return; set_default: *v->var = v->value; return; } /* show_integer()-- Show an integer environment variable */ static void show_integer (variable * v) { st_printf ("%s %d\n", var_source (v), *v->var); } /* init_boolean()-- Initialize a boolean environment variable. We * only look at the first letter of the variable. */ static void init_boolean (variable * v) { char *p; p = getenv (v->name); if (p == NULL) goto set_default; if (*p == '1' || *p == 'Y' || *p == 'y') { *v->var = 1; return; } if (*p == '0' || *p == 'N' || *p == 'n') { *v->var = 0; return; } v->bad = 1; set_default: *v->var = v->value; return; } /* show_boolean()-- Show a boolean environment variable */ static void show_boolean (variable * v) { st_printf ("%s %s\n", var_source (v), *v->var ? "Yes" : "No"); } /* init_mem()-- Initialize environment variables that have to do with * how memory from an ALLOCATE statement is filled. A single flag * enables filling and a second variable gives the value that is used * to initialize the memory. */ static void init_mem (variable * v) { int offset, n; char *p; p = getenv (v->name); options.allocate_init_flag = 0; /* The default */ if (p == NULL) return; if (strcasecmp (p, "NONE") == 0) return; /* IEEE-754 Quiet Not-a-Number that will work for single and double * precision. Look for the 'f95' mantissa in debug dumps. */ if (strcasecmp (p, "NaN") == 0) { options.allocate_init_flag = 1; options.allocate_init_value = 0xfff80f95; return; } /* Interpret the string as a hexadecimal constant */ n = 0; while (*p) { if (!isxdigit (*p)) { v->bad = 1; return; } offset = '0'; if (islower (*p)) offset = 'a'; if (isupper (*p)) offset = 'A'; n = (n << 4) | (*p++ - offset); } options.allocate_init_flag = 1; options.allocate_init_value = n; } static void show_mem (variable * v) { char *p; p = getenv (v->name); st_printf ("%s ", var_source (v)); if (options.allocate_init_flag) st_printf ("0x%x", options.allocate_init_value); st_printf ("\n"); } static void init_sep (variable * v) { int seen_comma; char *p; p = getenv (v->name); if (p == NULL) goto set_default; v->bad = 1; options.separator = p; options.separator_len = strlen (p); /* Make sure the separator is valid */ if (options.separator_len == 0) goto set_default; seen_comma = 0; while (*p) { if (*p == ',') { if (seen_comma) goto set_default; seen_comma = 1; p++; continue; } if (*p++ != ' ') goto set_default; } v->bad = 0; return; set_default: options.separator = " "; options.separator_len = 1; } static void show_sep (variable * v) { st_printf ("%s \"%s\"\n", var_source (v), options.separator); } static void init_string (variable * v) { } static void show_string (variable * v) { const char *p; p = getenv (v->name); if (p == NULL) p = ""; st_printf ("%s \"%s\"\n", var_source (v), p); } /* Structure for associating names and values. */ typedef struct { const char *name; int value; } choice; enum { FP_ROUND_NEAREST, FP_ROUND_UP, FP_ROUND_DOWN, FP_ROUND_ZERO }; static choice rounding[] = { {"NEAREST", FP_ROUND_NEAREST}, {"UP", FP_ROUND_UP}, {"DOWN", FP_ROUND_DOWN}, {"ZERO", FP_ROUND_ZERO}, {NULL} }; static choice precision[] = { { "24", 1}, { "53", 2}, { "64", 0}, { NULL} }; static choice signal_choices[] = { { "IGNORE", 1}, { "ABORT", 0}, { NULL} }; static void init_choice (variable * v, choice * c) { char *p; p = getenv (v->name); if (p == NULL) goto set_default; for (; c->name; c++) if (strcasecmp (c->name, p) == 0) break; if (c->name == NULL) { v->bad = 1; goto set_default; } *v->var = c->value; return; set_default: *v->var = v->value; } static void show_choice (variable * v, choice * c) { st_printf ("%s ", var_source (v)); for (; c->name; c++) if (c->value == *v->var) break; if (c->name) st_printf ("%s\n", c->name); else st_printf ("(Unknown)\n"); } static void init_round (variable * v) { init_choice (v, rounding); } static void show_round (variable * v) { show_choice (v, rounding); } static void init_precision (variable * v) { init_choice (v, precision); } static void show_precision (variable * v) { show_choice (v, precision); } static void init_signal (variable * v) { init_choice (v, signal_choices); } static void show_signal (variable * v) { show_choice (v, signal_choices); } static variable variable_table[] = { {"GFORTRAN_STDIN_UNIT", 5, &options.stdin_unit, init_integer, show_integer, "Unit number that will be preconnected to standard input\n" "(No preconnection if negative)"}, {"GFORTRAN_STDOUT_UNIT", 6, &options.stdout_unit, init_integer, show_integer, "Unit number that will be preconnected to standard output\n" "(No preconnection if negative)"}, {"GFORTRAN_USE_STDERR", 1, &options.use_stderr, init_boolean, show_boolean, "Sends library output to standard error instead of standard output."}, {"GFORTRAN_TMPDIR", 0, NULL, init_string, show_string, "Directory for scratch files. Overrides the TMP environment variable\n" "If TMP is not set " DEFAULT_TEMPDIR " is used."}, {"GFORTRAN_UNBUFFERED_ALL", 0, &options.all_unbuffered, init_boolean, show_boolean, "If TRUE, all output is unbuffered. This will slow down large writes " "but can be\nuseful for forcing data to be displayed immediately."}, {"GFORTRAN_SHOW_LOCUS", 1, &options.locus, init_boolean, show_boolean, "If TRUE, print filename and line number where runtime errors happen."}, /* GFORTRAN_NAME_xx (where xx is a unit number) gives the names of files * preconnected to those units. */ /* GFORTRAN_UNBUFFERED_xx (where xx is a unit number) gives a boolean that is used * to turn off buffering for that unit. */ {"GFORTRAN_OPTIONAL_PLUS", 0, &options.optional_plus, init_boolean, show_boolean, "Print optional plus signs in numbers where permitted. Default FALSE."}, {"GFORTRAN_DEFAULT_RECL", DEFAULT_RECL, &options.default_recl, init_integer, show_integer, "Default maximum record length for sequential files. Most useful for\n" "adjusting line length of preconnected units. Default " stringize (DEFAULT_RECL)}, {"GFORTRAN_LIST_SEPARATOR", 0, NULL, init_sep, show_sep, "Separatator to use when writing list output. May contain any number of " "spaces\nand at most one comma. Default is a single space."}, /* Memory related controls */ {"GFORTRAN_MEM_INIT", 0, NULL, init_mem, show_mem, "How to initialize allocated memory. Default value is NONE for no " "initialization\n(faster), NAN for a Not-a-Number with the mantissa " "0x40f95 or a custom\nhexadecimal value"}, {"GFORTRAN_MEM_CHECK", 0, &options.mem_check, init_boolean, show_boolean, "Whether memory still allocated will be reported when the program ends."}, /* Signal handling (Unix). */ {"GFORTRAN_SIGHUP", 0, &options.sighup, init_signal, show_signal, "Whether the program will IGNORE or ABORT on SIGHUP."}, {"GFORTRAN_SIGINT", 0, &options.sigint, init_signal, show_signal, "Whether the program will IGNORE or ABORT on SIGINT."}, /* Floating point control */ {"GFORTRAN_FPU_ROUND", 0, &options.fpu_round, init_round, show_round, "Set floating point rounding. Values are NEAREST, UP, DOWN, ZERO."}, {"GFORTRAN_FPU_PRECISION", 0, &options.fpu_precision, init_precision, show_precision, "Precision of intermediate results. Values are 24, 53 and 64."}, {"GFORTRAN_FPU_INVALID", 1, &options.fpu_invalid, init_boolean, show_boolean, "Raise a floating point exception on invalid FP operation."}, {"GFORTRAN_FPU_DENORMAL", 1, &options.fpu_denormal, init_boolean, show_boolean, "Raise a floating point exception when denormal numbers are encountered."}, {"GFORTRAN_FPU_ZERO", 0, &options.fpu_zerodiv, init_boolean, show_boolean, "Raise a floating point exception when dividing by zero."}, {"GFORTRAN_FPU_OVERFLOW", 0, &options.fpu_overflow, init_boolean, show_boolean, "Raise a floating point exception on overflow."}, {"GFORTRAN_FPU_UNDERFLOW", 0, &options.fpu_underflow, init_boolean, show_boolean, "Raise a floating point exception on underflow."}, {"GFORTRAN_FPU_PRECISION", 0, &options.fpu_precision_loss, init_boolean, show_boolean, "Raise a floating point exception on precision loss."}, {NULL} }; /* init_variables()-- Initialize most runtime variables from * environment variables. */ void init_variables (void) { variable *v; for (v = variable_table; v->name; v++) v->init (v); } /* check_buffered()-- Given an unit number n, determine if an override * for the stream exists. Returns zero for unbuffered, one for * buffered or two for not set. */ int check_buffered (int n) { char name[40]; variable v; int rv; if (options.all_unbuffered) return 0; strcpy (name, "GFORTRAN_UNBUFFERED_"); strcat (name, itoa (n)); v.name = name; v.value = 2; v.var = &rv; init_boolean (&v); return rv; } /* pattern_scan()-- Given an environment string, check that the name * has the same name as the pattern followed by an integer. On a * match, a pointer to the value is returned and the integer pointed * to by n is updated. Returns NULL on no match. */ static char * pattern_scan (char *env, const char *pattern, int *n) { char *p; size_t len; len = strlen (pattern); if (strncasecmp (env, pattern, len) != 0) return NULL; p = env + len; if (!isdigit (*p)) return NULL; while (isdigit (*p)) p++; if (*p != '=') return NULL; *p = '\0'; *n = atoi (env + len); *p++ = '='; return p; } void show_variables (void) { char *p, **e; variable *v; int n; /* TODO: print version number. */ st_printf ("GNU Fortran 95 runtime library version " "UNKNOWN" "\n\n"); st_printf ("Environment variables:\n"); st_printf ("----------------------\n"); for (v = variable_table; v->name; v++) { n = st_printf ("%s", v->name); print_spaces (25 - n); if (v->show == show_integer) st_printf ("Integer "); else if (v->show == show_boolean) st_printf ("Boolean "); else st_printf ("String "); v->show (v); st_printf ("%s\n\n", v->desc); } st_printf ("\nDefault unit names (GFORTRAN_NAME_x):\n"); for (e = environ; *e; e++) { p = pattern_scan (*e, "GFORTRAN_NAME_", &n); if (p == NULL) continue; st_printf ("GFORTRAN_NAME_%d %s\n", n, p); } st_printf ("\nUnit buffering overrides (GFORTRAN_UNBUFFERED_x):\n"); for (e = environ; *e; e++) { p = pattern_scan (*e, "GFORTRAN_UNBUFFERED_", &n); if (p == NULL) continue; st_printf ("GFORTRAN_UNBUFFERED_%d = %s\n", n, p); } /* System error codes */ st_printf ("\nRuntime error codes:"); st_printf ("\n--------------------\n"); for (n = ERROR_FIRST + 1; n < ERROR_LAST; n++) if (n < 0 || n > 9) st_printf ("%d %s\n", n, translate_error (n)); else st_printf (" %d %s\n", n, translate_error (n)); st_printf ("\nCommand line arguments:\n"); st_printf (" --help Print this list\n"); /* st_printf(" --resume Resume program execution from dropfile\n"); */ sys_exit (0); }