2018-05-21 Steven G. Kargl <kargl@gcc.gnu.org>
[official-gcc.git] / libgfortran / runtime / environ.c
blob22faad348da660b1645a14877170c3e255a7a5c6
1 /* Copyright (C) 2002-2018 Free Software Foundation, Inc.
2 Contributed by Andy Vaught
4 This file is part of the GNU Fortran runtime library (libgfortran).
6 Libgfortran is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
11 Libgfortran is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 Under Section 7 of GPL version 3, you are granted additional
17 permissions described in the GCC Runtime Library Exception, version
18 3.1, as published by the Free Software Foundation.
20 You should have received a copy of the GNU General Public License and
21 a copy of the GCC Runtime Library Exception along with this program;
22 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 <http://www.gnu.org/licenses/>. */
25 #include "libgfortran.h"
27 #include <string.h>
28 #include <strings.h>
29 #include <ctype.h>
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
36 /* Implementation of secure_getenv() for targets where it is not
37 provided. */
39 #ifdef FALLBACK_SECURE_GETENV
41 #if SUPPORTS_WEAKREF && defined(HAVE___SECURE_GETENV)
42 static char* weak_secure_getenv (const char*)
43 __attribute__((__weakref__("__secure_getenv")));
44 #endif
46 char *
47 secure_getenv (const char *name)
49 #if SUPPORTS_WEAKREF && defined(HAVE___SECURE_GETENV)
50 if (weak_secure_getenv)
51 return weak_secure_getenv (name);
52 #endif
54 if ((getuid () == geteuid ()) && (getgid () == getegid ()))
55 return getenv (name);
56 else
57 return NULL;
59 #endif
63 /* Examine the environment for controlling aspects of the program's
64 execution. Our philosophy here that the environment should not prevent
65 the program from running, so any invalid value will be ignored. */
68 options_t options;
70 typedef struct variable
72 const char *name;
73 int default_value;
74 int *var;
75 void (*init) (struct variable *);
77 variable;
79 static void init_unformatted (variable *);
82 /* Initialize an integer environment variable. */
84 static void
85 init_integer (variable * v)
87 char *p, *q;
89 p = getenv (v->name);
90 if (p == NULL)
91 return;
93 for (q = p; *q; q++)
94 if (!isdigit (*q) && (p != q || *q != '-'))
95 return;
97 *v->var = atoi (p);
101 /* Initialize an integer environment variable which has to be positive. */
103 static void
104 init_unsigned_integer (variable * v)
106 char *p, *q;
108 p = getenv (v->name);
109 if (p == NULL)
110 return;
112 for (q = p; *q; q++)
113 if (!isdigit (*q))
114 return;
116 *v->var = atoi (p);
120 /* Initialize a boolean environment variable. We only look at the first
121 letter of the value. */
123 static void
124 init_boolean (variable * v)
126 char *p;
128 p = getenv (v->name);
129 if (p == NULL)
130 return;
132 if (*p == '1' || *p == 'Y' || *p == 'y')
133 *v->var = 1;
134 else if (*p == '0' || *p == 'N' || *p == 'n')
135 *v->var = 0;
139 /* Initialize a list output separator. It may contain any number of spaces
140 and at most one comma. */
142 static void
143 init_sep (variable * v)
145 int seen_comma;
146 char *p;
148 p = getenv (v->name);
149 if (p == NULL)
150 goto set_default;
152 options.separator = p;
153 options.separator_len = strlen (p);
155 /* Make sure the separator is valid */
157 if (options.separator_len == 0)
158 goto set_default;
159 seen_comma = 0;
161 while (*p)
163 if (*p == ',')
165 if (seen_comma)
166 goto set_default;
167 seen_comma = 1;
168 p++;
169 continue;
172 if (*p++ != ' ')
173 goto set_default;
176 return;
178 set_default:
179 options.separator = " ";
180 options.separator_len = 1;
184 static variable variable_table[] = {
186 /* Unit number that will be preconnected to standard input */
187 { "GFORTRAN_STDIN_UNIT", GFC_STDIN_UNIT_NUMBER, &options.stdin_unit,
188 init_integer },
190 /* Unit number that will be preconnected to standard output */
191 { "GFORTRAN_STDOUT_UNIT", GFC_STDOUT_UNIT_NUMBER, &options.stdout_unit,
192 init_integer },
194 /* Unit number that will be preconnected to standard error */
195 { "GFORTRAN_STDERR_UNIT", GFC_STDERR_UNIT_NUMBER, &options.stderr_unit,
196 init_integer },
198 /* If TRUE, all output will be unbuffered */
199 { "GFORTRAN_UNBUFFERED_ALL", 0, &options.all_unbuffered, init_boolean },
201 /* If TRUE, output to preconnected units will be unbuffered */
202 { "GFORTRAN_UNBUFFERED_PRECONNECTED", 0, &options.unbuffered_preconnected,
203 init_boolean },
205 /* Whether to print filename and line number on runtime error */
206 { "GFORTRAN_SHOW_LOCUS", 1, &options.locus, init_boolean },
208 /* Print optional plus signs in numbers where permitted */
209 { "GFORTRAN_OPTIONAL_PLUS", 0, &options.optional_plus, init_boolean },
211 /* Separator to use when writing list output */
212 { "GFORTRAN_LIST_SEPARATOR", 0, NULL, init_sep },
214 /* Set the default data conversion for unformatted I/O */
215 { "GFORTRAN_CONVERT_UNIT", 0, 0, init_unformatted },
217 /* Print out a backtrace if possible on runtime error */
218 { "GFORTRAN_ERROR_BACKTRACE", -1, &options.backtrace, init_boolean },
220 { NULL, 0, NULL, NULL }
224 /* Initialize most runtime variables from
225 * environment variables. */
227 void
228 init_variables (void)
230 variable *v;
232 for (v = variable_table; v->name; v++)
234 if (v->var)
235 *v->var = v->default_value;
236 v->init (v);
241 /* This is the handling of the GFORTRAN_CONVERT_UNITS environment variable.
242 It is called from environ.c to parse this variable, and from
243 open.c to determine if the user specified a default for an
244 unformatted file.
245 The syntax of the environment variable is, in bison grammar:
247 GFORTRAN_CONVERT_UNITS: mode | mode ';' exception ;
248 mode: 'native' | 'swap' | 'big_endian' | 'little_endian' ;
249 exception: mode ':' unit_list | unit_list ;
250 unit_list: unit_spec | unit_list unit_spec ;
251 unit_spec: INTEGER | INTEGER '-' INTEGER ;
254 /* Defines for the tokens. Other valid tokens are ',', ':', '-'. */
257 #define NATIVE 257
258 #define SWAP 258
259 #define BIG 259
260 #define LITTLE 260
261 /* Some space for additional tokens later. */
262 #define INTEGER 273
263 #define END (-1)
264 #define ILLEGAL (-2)
266 typedef struct
268 int unit;
269 unit_convert conv;
270 } exception_t;
273 static char *p; /* Main character pointer for parsing. */
274 static char *lastpos; /* Auxiliary pointer, for backing up. */
275 static int unit_num; /* The last unit number read. */
276 static int unit_count; /* The number of units found. */
277 static int do_count; /* Parsing is done twice - first to count the number
278 of units, then to fill in the table. This
279 variable controls what to do. */
280 static exception_t *elist; /* The list of exceptions to the default. This is
281 sorted according to unit number. */
282 static int n_elist; /* Number of exceptions to the default. */
284 static unit_convert endian; /* Current endianness. */
286 static unit_convert def; /* Default as specified (if any). */
288 /* Search for a unit number, using a binary search. The
289 first argument is the unit number to search for. The second argument
290 is a pointer to an index.
291 If the unit number is found, the function returns 1, and the index
292 is that of the element.
293 If the unit number is not found, the function returns 0, and the
294 index is the one where the element would be inserted. */
296 static int
297 search_unit (int unit, int *ip)
299 int low, high, mid;
301 if (n_elist == 0)
303 *ip = 0;
304 return 0;
307 low = 0;
308 high = n_elist - 1;
312 mid = (low + high) / 2;
313 if (unit == elist[mid].unit)
315 *ip = mid;
316 return 1;
318 else if (unit > elist[mid].unit)
319 low = mid + 1;
320 else
321 high = mid - 1;
322 } while (low <= high);
324 if (unit > elist[mid].unit)
325 *ip = mid + 1;
326 else
327 *ip = mid;
329 return 0;
332 /* This matches a keyword. If it is found, return the token supplied,
333 otherwise return ILLEGAL. */
335 static int
336 match_word (const char *word, int tok)
338 int res;
340 if (strncasecmp (p, word, strlen (word)) == 0)
342 p += strlen (word);
343 res = tok;
345 else
346 res = ILLEGAL;
347 return res;
350 /* Match an integer and store its value in unit_num. This only works
351 if p actually points to the start of an integer. The caller has
352 to ensure this. */
354 static int
355 match_integer (void)
357 unit_num = 0;
358 while (isdigit (*p))
359 unit_num = unit_num * 10 + (*p++ - '0');
360 return INTEGER;
363 /* This reads the next token from the GFORTRAN_CONVERT_UNITS variable.
364 Returned values are the different tokens. */
366 static int
367 next_token (void)
369 int result;
371 lastpos = p;
372 switch (*p)
374 case '\0':
375 result = END;
376 break;
378 case ':':
379 case ',':
380 case '-':
381 case ';':
382 result = *p;
383 p++;
384 break;
386 case 'b':
387 case 'B':
388 result = match_word ("big_endian", BIG);
389 break;
391 case 'l':
392 case 'L':
393 result = match_word ("little_endian", LITTLE);
394 break;
396 case 'n':
397 case 'N':
398 result = match_word ("native", NATIVE);
399 break;
401 case 's':
402 case 'S':
403 result = match_word ("swap", SWAP);
404 break;
406 case '1': case '2': case '3': case '4': case '5':
407 case '6': case '7': case '8': case '9':
408 result = match_integer ();
409 break;
411 default:
412 result = ILLEGAL;
413 break;
415 return result;
418 /* Back up the last token by setting back the character pointer. */
420 static void
421 push_token (void)
423 p = lastpos;
426 /* This is called when a unit is identified. If do_count is nonzero,
427 increment the number of units by one. If do_count is zero,
428 put the unit into the table. */
430 static void
431 mark_single (int unit)
433 int i,j;
435 if (do_count)
437 unit_count++;
438 return;
440 if (search_unit (unit, &i))
442 elist[i].conv = endian;
444 else
446 for (j=n_elist-1; j>=i; j--)
447 elist[j+1] = elist[j];
449 n_elist += 1;
450 elist[i].unit = unit;
451 elist[i].conv = endian;
455 /* This is called when a unit range is identified. If do_count is
456 nonzero, increase the number of units. If do_count is zero,
457 put the unit into the table. */
459 static void
460 mark_range (int unit1, int unit2)
462 int i;
463 if (do_count)
464 unit_count += abs (unit2 - unit1) + 1;
465 else
467 if (unit2 < unit1)
468 for (i=unit2; i<=unit1; i++)
469 mark_single (i);
470 else
471 for (i=unit1; i<=unit2; i++)
472 mark_single (i);
476 /* Parse the GFORTRAN_CONVERT_UNITS variable. This is called
477 twice, once to count the units and once to actually mark them in
478 the table. When counting, we don't check for double occurrences
479 of units. */
481 static int
482 do_parse (void)
484 int tok;
485 int unit1;
486 int continue_ulist;
487 char *start;
489 unit_count = 0;
491 start = p;
493 /* Parse the string. First, let's look for a default. */
494 tok = next_token ();
495 switch (tok)
497 case NATIVE:
498 endian = GFC_CONVERT_NATIVE;
499 break;
501 case SWAP:
502 endian = GFC_CONVERT_SWAP;
503 break;
505 case BIG:
506 endian = GFC_CONVERT_BIG;
507 break;
509 case LITTLE:
510 endian = GFC_CONVERT_LITTLE;
511 break;
513 case INTEGER:
514 /* A leading digit means that we are looking at an exception.
515 Reset the position to the beginning, and continue processing
516 at the exception list. */
517 p = start;
518 goto exceptions;
519 break;
521 case END:
522 goto end;
523 break;
525 default:
526 goto error;
527 break;
530 tok = next_token ();
531 switch (tok)
533 case ';':
534 def = endian;
535 break;
537 case ':':
538 /* This isn't a default after all. Reset the position to the
539 beginning, and continue processing at the exception list. */
540 p = start;
541 goto exceptions;
542 break;
544 case END:
545 def = endian;
546 goto end;
547 break;
549 default:
550 goto error;
551 break;
554 exceptions:
556 /* Loop over all exceptions. */
557 while(1)
559 tok = next_token ();
560 switch (tok)
562 case NATIVE:
563 if (next_token () != ':')
564 goto error;
565 endian = GFC_CONVERT_NATIVE;
566 break;
568 case SWAP:
569 if (next_token () != ':')
570 goto error;
571 endian = GFC_CONVERT_SWAP;
572 break;
574 case LITTLE:
575 if (next_token () != ':')
576 goto error;
577 endian = GFC_CONVERT_LITTLE;
578 break;
580 case BIG:
581 if (next_token () != ':')
582 goto error;
583 endian = GFC_CONVERT_BIG;
584 break;
586 case INTEGER:
587 push_token ();
588 break;
590 case END:
591 goto end;
592 break;
594 default:
595 goto error;
596 break;
598 /* We arrive here when we want to parse a list of
599 numbers. */
600 continue_ulist = 1;
603 tok = next_token ();
604 if (tok != INTEGER)
605 goto error;
607 unit1 = unit_num;
608 tok = next_token ();
609 /* The number can be followed by a - and another number,
610 which means that this is a unit range, a comma
611 or a semicolon. */
612 if (tok == '-')
614 if (next_token () != INTEGER)
615 goto error;
617 mark_range (unit1, unit_num);
618 tok = next_token ();
619 if (tok == END)
620 goto end;
621 else if (tok == ';')
622 continue_ulist = 0;
623 else if (tok != ',')
624 goto error;
626 else
628 mark_single (unit1);
629 switch (tok)
631 case ';':
632 continue_ulist = 0;
633 break;
635 case ',':
636 break;
638 case END:
639 goto end;
640 break;
642 default:
643 goto error;
646 } while (continue_ulist);
648 end:
649 return 0;
650 error:
651 def = GFC_CONVERT_NONE;
652 return -1;
655 void init_unformatted (variable * v)
657 char *val;
658 val = getenv (v->name);
659 def = GFC_CONVERT_NONE;
660 n_elist = 0;
662 if (val == NULL)
663 return;
664 do_count = 1;
665 p = val;
666 do_parse ();
667 if (do_count <= 0)
669 n_elist = 0;
670 elist = NULL;
672 else
674 elist = xmallocarray (unit_count, sizeof (exception_t));
675 do_count = 0;
676 p = val;
677 do_parse ();
681 /* Get the default conversion for for an unformatted unit. */
683 unit_convert
684 get_unformatted_convert (int unit)
686 int i;
688 if (elist == NULL)
689 return def;
690 else if (search_unit (unit, &i))
691 return elist[i].conv;
692 else
693 return def;