//! @file a68g-options.c
//! @author J. Marcel van der Veer

//! @section Copyright
//!
//! This file is part of Algol68G - an Algol 68 compiler-interpreter.
//! Copyright 2001-2026 J. Marcel van der Veer [algol68g@xs4all.nl].

//! @section License
//!
//! This program 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 3 of the License, or 
//! (at your option) any later version.
//!
//! This program 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 this program. If not, see [http://www.gnu.org/licenses/].

//! @section Synopsis
//!
//! Algol 68 Genie options.

#include "a68g.h"
#include "a68g-prelude.h"
#include "a68g-mp.h"
#include "a68g-options.h"
#include "a68g-parser.h"

// This code options to Algol68G.
// 
// Option syntax does not follow GNU standards.
// 
// Options come from:
//   [1] A rc file (normally .a68grc).
//   [2] The A68G_OPTIONS environment variable overrules [1].
//   [3] Command line options overrule [2].
//   [4] Pragmat items overrule [3]. 

//! @brief Strip minus preceeding a string.

char *strip_sign (char *p)
{
  if (p != NO_TEXT) {
    char *q = p;
    while (q[0] == '-') {
      q++;
    }
    if (strlen (q) > 0) {
      return new_string (q, NO_TEXT);
    }
  }
  return p;
}

//! @brief Error handler for options.

void option_error (LINE_T * l, char *option, char *info)
{
  if (option != NO_TEXT) {
    ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "*at option \"%s\"", option) >= 0);
    if (info != NO_TEXT) {
      ASSERT (a68g_bufprt (A68G (edit_line), SNPRINTF_SIZE, "%s, %s", A68G (output_line), info) >= 0);
    } else {
      ASSERT (a68g_bufprt (A68G (edit_line), SNPRINTF_SIZE, "%s", A68G (output_line)) >= 0);
    }
  } else if (info != NO_TEXT) {
    ASSERT (a68g_bufprt (A68G (edit_line), SNPRINTF_SIZE, "%s", info) >= 0);
  } else {
    ASSERT (a68g_bufprt (A68G (edit_line), SNPRINTF_SIZE, "in options") >= 0);
  }
  scan_error (l, NO_TEXT, A68G (edit_line));
}

//! @brief Check overflow at integer multiplication.

BOOL_T int_mul_overflow (UNSIGNED_T u, UNSIGNED_T v, UNSIGNED_T max_int)
{
  if (u == 0 || v == 0) {
    return (A68G_FALSE);
  } else {
    return v > max_int / u;
  }
}

//! @brief Set default core size.

void default_mem_sizes (INT_T n, LINE_T *start_l, char *start_c)
{
  #define SET_SIZE(m, n) {\
    if (int_mul_overflow (n, A68G_MEGA, MAX_MEM_SIZE)) {\
      option_error (start_l, start_c, ERROR_VALUE_TOO_LARGE);\
      return;\
    } else if ((n) * A68G_MEGA + A68G (storage_overhead) > MAX_MEM_SIZE) {\
      option_error (start_l, start_c, ERROR_VALUE_TOO_LARGE);\
      return;\
    } else {\
      (m) = (n) * A68G_MEGA + A68G (storage_overhead);\
    }}

  n = MAX (n, 1);
  A68G (storage_overhead) = MEM_OVERHEAD;
  SET_SIZE (A68G (frame_stack_size), 10 * n);
  SET_SIZE (A68G (expr_stack_size), 5 * n);
  SET_SIZE (A68G (heap_size), 65 * n);
  SET_SIZE (A68G (handle_pool_size), 20 * n);
#undef SET_SIZE
}

//! @brief Read options from the .rc file.

void read_rc_options (void)
{
  BUFFER name, new_name;
  BUFCLR (name);
  BUFCLR (new_name);
  ASSERT (a68g_bufprt (name, SNPRINTF_SIZE, ".%src", A68G (a68g_cmd_name)) >= 0);
  FILE *f = a68g_fopen (name, "r", new_name);
  if (f != NO_FILE) {
    while (!feof (f)) {
      if (fgets (A68G (input_line), BUFFER_SIZE, f) != NO_TEXT) {
        size_t len = strlen (A68G (input_line));
        if (len > 0 && A68G (input_line)[len - 1] == NEWLINE_CHAR) {
          A68G (input_line)[len - 1] = NULL_CHAR;
        }
        isolate_options (A68G (input_line), NO_LINE);
      }
    }
    ASSERT (fclose (f) == 0);
    (void) set_options (OPTION_LIST (&A68G_JOB), A68G_FALSE);
  } else {
    errno = 0;
  }
}

//! @brief Read options from A68G_OPTIONS.

void read_env_options (void)
{
  if (getenv ("A68G_OPTIONS") != NULL) {
    isolate_options (getenv ("A68G_OPTIONS"), NO_LINE);
    (void) set_options (OPTION_LIST (&A68G_JOB), A68G_FALSE);
    errno = 0;
  }
}

//! @brief Tokenise string 'p' that holds options.

void isolate_options (char *p, LINE_T * line)
{
  while (p != NO_TEXT && p[0] != NULL_CHAR) {
// Skip white space etc.
    while ((p[0] == BLANK_CHAR || p[0] == TAB_CHAR || p[0] == ',' || p[0] == NEWLINE_CHAR) && p[0] != NULL_CHAR) {
      p++;
    }
// ... then tokenise an item.
    if (p[0] != NULL_CHAR) {
      char *q;
// Item can be "string". Note that these are not A68 strings.
      if (p[0] == QUOTE_CHAR || p[0] == '\'' || p[0] == '`') {
        char delim = p[0];
        p++;
// 'q' points at first significant char in item.
        q = p;
        while (p[0] != delim && p[0] != NULL_CHAR) {
          p++;
        }
        if (p[0] != NULL_CHAR) {
          p[0] = NULL_CHAR;     // p[0] was delimiter
          p++;
        } else {
          scan_error (line, NO_TEXT, ERROR_UNTERMINATED_STRING);
        }
      } else {
// Item is not a delimited string.
        q = p;
// Tokenise symbol and gather it in the option list for later processing.
// Skip '='s, we accept if someone writes -prec=60 -heap=8192
        if (*q == '=') {
          p++;
        } else {
// Skip item 
          while (p[0] != BLANK_CHAR && p[0] != NULL_CHAR && p[0] != '=' && p[0] != ',' && p[0] != NEWLINE_CHAR) {
            p++;
          }
        }
        if (p[0] != NULL_CHAR) {
          p[0] = NULL_CHAR;
          p++;
        }
      }
// 'q' points to first significant char in item, and 'p' points after item.
      add_option_list (&(OPTION_LIST (&A68G_JOB)), q, line);
    }
  }
}

//! @brief Set default values for options.

void default_options (MODULE_T * p)
{
  OPTION_BACKTRACE (p) = A68G_FALSE;
  OPTION_BRACKETS (p) = A68G_FALSE;
  OPTION_CHECK_ONLY (p) = A68G_FALSE;
  OPTION_CLOCK (p) = A68G_FALSE;
  OPTION_COMPILE_CHECK (p) = A68G_FALSE;
  OPTION_COMPILE (p) = A68G_FALSE;
  OPTION_CONSERVATIVE_GC (p) = A68G_GC_GO;
  OPTION_CROSS_REFERENCE (p) = A68G_FALSE;
  OPTION_DEBUG (p) = A68G_FALSE;
  OPTION_FOLD (p) = A68G_FALSE;
  OPTION_INDENT (p) = 2;
  OPTION_KEEP (p) = A68G_FALSE;
  OPTION_LICENSE (p) = A68G_FALSE;
  OPTION_MOID_LISTING (p) = A68G_FALSE;
  OPTION_NODEMASK (p) = (STATUS_MASK_T) (ASSERT_MASK | SOURCE_MASK);
  OPTION_NO_NOTICES (p) = A68G_TRUE;
  OPTION_NO_WARNINGS (p) = A68G_FALSE;
  OPTION_OPT_LEVEL (p) = NO_OPTIMISE;
  OPTION_PORTCHECK (p) = A68G_FALSE;
  OPTION_PRAGMAT_SEMA (p) = A68G_TRUE;
  OPTION_PRETTY (p) = A68G_FALSE;
  OPTION_QUIET (p) = A68G_FALSE;
  OPTION_REDUCTIONS (p) = A68G_FALSE;
  OPTION_REGRESSION_TEST (p) = A68G_FALSE;
  OPTION_RERUN (p) = A68G_FALSE;
  OPTION_RESTART (p) = A68G_TRUE;
  OPTION_RUN (p) = A68G_FALSE;
  OPTION_RUN_SCRIPT (p) = A68G_FALSE;
  OPTION_SOURCE_LISTING (p) = A68G_FALSE;
  OPTION_STANDARD_PRELUDE_LISTING (p) = A68G_FALSE;
  OPTION_STATISTICS_LISTING (p) = A68G_FALSE;
  OPTION_STRICT (p) = A68G_FALSE;
  OPTION_STROPPING (p) = UPPER_STROPPING;
  OPTION_TIME_LIMIT (p) = 0;
  OPTION_TRACE (p) = A68G_FALSE;
  OPTION_TREE_LISTING (p) = A68G_FALSE;
  OPTION_UNUSED (p) = A68G_FALSE;
  OPTION_VERBOSE (p) = A68G_FALSE;
  OPTION_VERSION (p) = A68G_FALSE;
  set_long_mp_digits (0);
}

//! @brief Add an option to the list, to be processed later.

void add_option_list (OPTION_LIST_T ** l, char *str, LINE_T * line)
{
  if (*l == NO_OPTION_LIST) {
    *l = (OPTION_LIST_T *) get_heap_space (SIZE_ALIGNED (OPTION_LIST_T));
    SCAN (*l) = SOURCE_SCAN (&A68G_JOB);
    STR (*l) = new_string (str, NO_TEXT);
    PROCESSED (*l) = A68G_FALSE;
    LINE (*l) = line;
    NEXT (*l) = NO_OPTION_LIST;
  } else {
    add_option_list (&(NEXT (*l)), str, line);
  }
}

//! @brief Free an option list.

void free_option_list (OPTION_LIST_T * l)
{
  if (l != NO_OPTION_LIST) {
    free_option_list (NEXT (l));
    a68g_free (STR (l));
    a68g_free (l);
  }
}

//! @brief Initialise option handler.

void init_options (void)
{
  A68G (options) = (OPTIONS_T *) a68g_alloc (SIZE_ALIGNED (OPTIONS_T), __func__, __LINE__);
  OPTION_LIST (&A68G_JOB) = NO_OPTION_LIST;
}

//! @brief Test equality of p and q, upper case letters in q are mandatory.

static inline BOOL_T eq (char *p, char *q)
{
// Upper case letters in 'q' are mandatory, lower case must match.
  if (OPTION_PRAGMAT_SEMA (&A68G_JOB)) {
    return match_string (p, q, '=');
  } else {
    return A68G_FALSE;
  }
}

//! @brief Process echoes gathered in the option list.

void prune_echoes (OPTION_LIST_T * ol)
{
  while (ol != NO_OPTION_LIST) {
    if (SCAN (ol) == SOURCE_SCAN (&A68G_JOB)) {
      char *full_option = STR (ol);
      char *option = strip_sign (full_option);
// ECHO echoes a string.
      if (eq (option, "ECHO")) {
        {
          char *car = strchr (option, '=');
          if (car != NO_TEXT) {
            io_close_tty_line ();
            ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%s", &car[1]) >= 0);
            WRITE (A68G_STDOUT, A68G (output_line));
          } else {
            FORWARD (ol);
            if (ol != NO_OPTION_LIST) {
              if (strcmp (STR (ol), "=") == 0) {
                FORWARD (ol);
              }
              if (ol != NO_OPTION_LIST) {
                io_close_tty_line ();
                ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%s", STR (ol)) >= 0);
                WRITE (A68G_STDOUT, A68G (output_line));
              }
            }
          }
        }
      }
    }
    if (ol != NO_OPTION_LIST) {
      FORWARD (ol);
    }
  }
}

//! @brief Translate integral option argument.

static UNSIGNED_T fetch_integral (char *p, OPTION_LIST_T ** i, BOOL_T * error, UNSIGNED_T max_int)
{
  LINE_T *start_l = LINE (*i);
  char *start_c = STR (*i);
  char *car = NO_TEXT, *num = NO_TEXT;
  *error = A68G_FALSE;
// Fetch argument.
  car = strchr (p, '=');
  if (car == NO_TEXT) {
    FORWARD (*i);
    *error = (BOOL_T) (*i == NO_OPTION_LIST);
    if (!*error && strcmp (STR (*i), "=") == 0) {
      FORWARD (*i);
      *error = (BOOL_T) (*i == NO_OPTION_LIST);
    }
    if (!*error) {
      num = STR (*i);
    }
  } else {
    num = &car[1];
    *error = (BOOL_T) (num[0] == NULL_CHAR);
  }
// Translate argument into integer.
  if (*error) {
    option_error (start_l, start_c, ERROR_MISSING_STUFF);
    return 0;
  } else {
    if (num[0] == '-') {
      option_error (start_l, start_c, ERROR_INVALID_VALUE);
      return 0;
    }
    char *suffix;
    errno = 0;
    #if (A68G_LEVEL >= 3)
      UNSIGNED_T mult = 1, value = (UNSIGNED_T) strtoul (num, &suffix, 0);
    #else
      UNSIGNED_T mult = 1, value = (UNSIGNED_T) strtoull (num, &suffix, 0);
    #endif
    *error = (BOOL_T) (suffix == num);
    if (errno != 0 || *error) {
      option_error (start_l, start_c, ERROR_INVALID_VALUE);
      *error = A68G_TRUE;
    } else if (value < 0) {
      option_error (start_l, start_c, ERROR_INVALID_VALUE);
      *error = A68G_TRUE;
    } else {
// Accept suffix multipliers: 32k, 64M, 1G, (2T, 1P).
      if (suffix != NO_TEXT) {
        switch (suffix[0]) {
        case NULL_CHAR: {
            mult = 1;
            break;
          }
        case 'k':
        case 'K': {
            mult = A68G_KILO;
            break;
          }
        case 'm':
        case 'M': {
            mult = A68G_MEGA;
            break;
          }
        case 'g':
        case 'G': {
            mult = A68G_GIGA;
            break;
          }
        #if defined (A68G_TERA)
          case 't':
          case 'T': {
              mult = A68G_TERA;
              break;
            }
        #endif
        #if defined (A68G_PETA)
          case 'p':
          case 'P': {
              mult = A68G_PETA;
              break;
            }
        #endif
        default: {
            option_error (start_l, start_c, ERROR_INVALID_VALUE);
            *error = A68G_TRUE;
            break;
          }
        }
        if (suffix[0] != NULL_CHAR && suffix[1] != NULL_CHAR) {
          option_error (start_l, start_c, ERROR_INVALID_VALUE);
          *error = A68G_TRUE;
        }
      }
    }
// Check overflow.
    if (int_mul_overflow (value, mult, max_int)) {
      option_error (start_l, start_c, ERROR_VALUE_TOO_LARGE);
      return 0;
    } else {
      return value * mult;
    }
  }
}

//! @brief Dump technical information.

static void tech_stuff (void)
{
  state_version (A68G_STDOUT);
  ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (A68G_REF) = " A68G_LU, (UNSIGNED_T) sizeof (A68G_REF)) >= 0);
  WRITELN (A68G_STDOUT, A68G (output_line));
  ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (A68G_PROCEDURE) = " A68G_LU, (UNSIGNED_T) sizeof (A68G_PROCEDURE)) >= 0);
  WRITELN (A68G_STDOUT, A68G (output_line));
  #if (A68G_LEVEL >= 3)
    ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (DOUBLE_T) = " A68G_LU, (UNSIGNED_T) sizeof (DOUBLE_T)) >= 0);
    WRITELN (A68G_STDOUT, A68G (output_line));
    ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (DOUBLE_NUM_T) = " A68G_LU, (UNSIGNED_T) sizeof (DOUBLE_NUM_T)) >= 0);
    WRITELN (A68G_STDOUT, A68G (output_line));
  #endif
  ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (A68G_INT) = " A68G_LU, (UNSIGNED_T) sizeof (A68G_INT)) >= 0);
  WRITELN (A68G_STDOUT, A68G (output_line));
  ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (A68G_REAL) = " A68G_LU, (UNSIGNED_T) sizeof (A68G_REAL)) >= 0);
  WRITELN (A68G_STDOUT, A68G (output_line));
  ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (A68G_BOOL) = " A68G_LU, (UNSIGNED_T) sizeof (A68G_BOOL)) >= 0);
  WRITELN (A68G_STDOUT, A68G (output_line));
  ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (A68G_CHAR) = " A68G_LU, (UNSIGNED_T) sizeof (A68G_CHAR)) >= 0);
  WRITELN (A68G_STDOUT, A68G (output_line));
  ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (A68G_BITS) = " A68G_LU, (UNSIGNED_T) sizeof (A68G_BITS)) >= 0);
  WRITELN (A68G_STDOUT, A68G (output_line));
  #if (A68G_LEVEL >= 3)
    ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (A68G_LONG_INT) = " A68G_LU, (UNSIGNED_T) sizeof (A68G_LONG_INT)) >= 0);
    WRITELN (A68G_STDOUT, A68G (output_line));
    ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (A68G_LONG_REAL) = " A68G_LU, (UNSIGNED_T) sizeof (A68G_LONG_REAL)) >= 0);
    WRITELN (A68G_STDOUT, A68G (output_line));
  #else
    ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (A68G_LONG_REAL) = " A68G_LU, (UNSIGNED_T) size_mp ()) >= 0);
    WRITELN (A68G_STDOUT, A68G (output_line));
  #endif
  ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (A68G_LONG_LONG_REAL) = " A68G_LU, (UNSIGNED_T) (UNSIGNED_T) SIZE_MP (LONG_LONG_MP_DIGITS)) >= 0);
  WRITELN (A68G_STDOUT, A68G (output_line));
  WRITELN (A68G_STDOUT, "");
  ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (INT_T) = " A68G_LU, (UNSIGNED_T) sizeof (INT_T)) >= 0);
  WRITELN (A68G_STDOUT, A68G (output_line));
  ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (UNSIGNED_T) = " A68G_LU, (UNSIGNED_T) sizeof (UNSIGNED_T)) >= 0);
  WRITELN (A68G_STDOUT, A68G (output_line));
  ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (a68g_off_t) = " A68G_LU, (UNSIGNED_T) sizeof (a68g_off_t)) >= 0);
  WRITELN (A68G_STDOUT, A68G (output_line));
  ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (size_t) = " A68G_LU, (UNSIGNED_T) sizeof (size_t)) >= 0);
  WRITELN (A68G_STDOUT, A68G (output_line));
  ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "sizeof (ssize_t) = " A68G_LU, (UNSIGNED_T) sizeof (ssize_t)) >= 0);
  WRITELN (A68G_STDOUT, A68G (output_line));
  ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "UPB size_t = " A68G_LU, (UNSIGNED_T) MAX_MEM_SIZE) >= 0);
  WRITELN (A68G_STDOUT, A68G (output_line));
  WRITELN (A68G_STDOUT, "");
  exit (EXIT_SUCCESS);
}

//! @brief Process options gathered in the option list.

BOOL_T need_library (OPTION_LIST_T *i)
{
  char *q = strip_sign (STR (i));
  if (eq (q, "compiler")) {
    #if defined (BUILD_A68G_COMPILER)
      return (A68G_TRUE);
    #else
      io_close_tty_line ();
      WRITE (A68G_STDERR, "plugin compiler required - exiting graciously");
      a68g_exit (EXIT_SUCCESS);
    #endif
  }
  if (eq (q, "curl")) {
    #if defined (HAVE_CURL)
      return (A68G_TRUE);
    #else
      io_close_tty_line ();
      WRITE (A68G_STDERR, "curl library required - exiting graciously");
      a68g_exit (EXIT_SUCCESS);
    #endif
  }
  if (eq (q, "curses")) {
    #if defined (HAVE_CURSES)
      return (A68G_TRUE);
    #else
      io_close_tty_line ();
      WRITE (A68G_STDERR, "curses required - exiting graciously");
      a68g_exit (EXIT_SUCCESS);
    #endif
  }
  if (eq (q, "gsl")) {
    #if defined (HAVE_GSL)
      return (A68G_TRUE);
    #else
      io_close_tty_line ();
      WRITE (A68G_STDERR, "GNU Scientific Library required - exiting graciously");
      a68g_exit (EXIT_SUCCESS);
    #endif
  }
  if (eq (q, "http")) {
    #if !defined (HAVE_CURL)
      io_close_tty_line ();
      WRITELN (A68G_STDERR, "curl required - exiting graciously");
      a68g_exit (EXIT_SUCCESS);
    #else
      return (A68G_TRUE);
    #endif
  }
  if (eq (q, "ieee")) {
    #if defined (HAVE_IEEE_754)
      return (A68G_TRUE);
    #else
      io_close_tty_line ();
      WRITE (A68G_STDERR, "IEEE required - exiting graciously");
      a68g_exit (EXIT_SUCCESS);
    #endif
  }
  if (eq (q, "linux")) {
    #if defined (BUILD_LINUX)
      return (A68G_TRUE);
    #else
      io_close_tty_line ();
      WRITE (A68G_STDERR, "linux required - exiting graciously");
      a68g_exit (EXIT_SUCCESS);
    #endif
  }
  if (eq (q, "mathlib")) {
    #if defined (HAVE_R_MATHLIB)
      return (A68G_TRUE);
    #else
      io_close_tty_line ();
      WRITE (A68G_STDERR, "R mathlib required - exiting graciously");
      a68g_exit (EXIT_SUCCESS);
    #endif
  }
  if (eq (q, "mpfr")) {
    #if defined (HAVE_GNU_MPFR)
      return (A68G_TRUE);
    #else
      io_close_tty_line ();
      WRITE (A68G_STDERR, "GNU MPFR required - exiting graciously");
      a68g_exit (EXIT_SUCCESS);
    #endif
  }
  if (eq (q, "plotutils")) {
    #if defined (HAVE_GNU_PLOTUTILS)
      return (A68G_TRUE);
    #else
      io_close_tty_line ();
      WRITE (A68G_STDERR, "GNU plotutils required - exiting graciously");
      a68g_exit (EXIT_SUCCESS);
    #endif
  }
  if (eq (q, "postgresql")) {
    #if defined (HAVE_POSTGRESQL)
      return (A68G_TRUE);
    #else
      io_close_tty_line ();
      WRITE (A68G_STDERR, "postgresql required - exiting graciously");
      a68g_exit (EXIT_SUCCESS);
    #endif
  }
  if (eq (q, "threads")) {
    #if defined (BUILD_PARALLEL_CLAUSE)
      return (A68G_TRUE);
    #else
      io_close_tty_line ();
      WRITE (A68G_STDERR, "POSIX threads required - exiting graciously");
      a68g_exit (EXIT_SUCCESS);
    #endif
  }
  return A68G_FALSE;
}

//! @brief Process options gathered in the option list.

BOOL_T set_options (OPTION_LIST_T *i, BOOL_T cmd_line)
{
  BOOL_T siga = A68G_TRUE, name_set = A68G_FALSE, skip = A68G_FALSE;
  OPTION_LIST_T *j = i;
  errno = 0;
  while (i != NO_OPTION_LIST && siga) {
// Once SCRIPT is processed we skip options on the command line.
    if (cmd_line && skip) {
      FORWARD (i);
    } else {
      LINE_T *start_l = LINE (i);
      size_t n = strlen (STR (i));
// Allow for spaces ending in # to have A68 comment syntax with '#!'.
      while (n > 0 && (IS_SPACE ((STR (i))[n - 1]) || (STR (i))[n - 1] == '#')) {
        (STR (i))[--n] = NULL_CHAR;
      }
      if (!(PROCESSED (i))) {
// Accept UNIX '-option [=] value'.
        char *full_option = STR (i);
        BOOL_T minus_sign = (BOOL_T) (full_option[0] == '-');
        char *option = strip_sign (full_option);
// Reserved case.
        if (!minus_sign && eq (option, "#")) {
          ;
        } 
// Item without minus sign on command line, is a filename.
        else if (!minus_sign && cmd_line) {
          if (!name_set) {
            FILE_INITIAL_NAME (&A68G_JOB) = new_string (option, NO_TEXT);
            name_set = A68G_TRUE;
          } else {
            option_error (NO_LINE, full_option, ERROR_MULTIPLE_SOURCE_FILES);
          }
        }
// Empty item '--' stops option processing.
        else if (strcmp (option, "--") == 0) {
          siga = A68G_FALSE;
        }
// Empty item '-' is incorrect.
        else if (strcmp (option, "-") == 0) {
          option_error (start_l, full_option, ERROR_INVALID_OPTION);
          siga = A68G_FALSE;
        }
// ALGOL60STROPPING sets stropping to quote stropping.
        else if (eq (option, "ALGOL60stropping")) {
          OPTION_STROPPING (&A68G_JOB) = QUOTE_STROPPING;
        } else if (eq (option, "ALGOL60-stropping")) {
          OPTION_STROPPING (&A68G_JOB) = QUOTE_STROPPING;
        }
// ASSERTIONS switches on/off the processing of assertions.
        else if (eq (option, "Assertions")) {
          OPTION_NODEMASK (&A68G_JOB) |= ASSERT_MASK;
        } else if (eq (option, "NOAssertions") || eq (option, "NO-Assertions")) {
          OPTION_NODEMASK (&A68G_JOB) &= ~ASSERT_MASK;
        }
// APROPOS, HELP, INFO give online help.
        else if ((eq (option, "APropos") || eq (option, "Help") || eq (option, "INfo")) && cmd_line) {
          FORWARD (i);
          if (i != NO_OPTION_LIST && strcmp (STR (i), "=") == 0) {
            FORWARD (i);
          }
          if (i != NO_OPTION_LIST) {
            apropos (A68G_STDOUT, NO_TEXT, STR (i));
          } else {
            apropos (A68G_STDOUT, NO_TEXT, "options");
          }
          a68g_exit (EXIT_SUCCESS);
        }
// BACKTRACE and NOBACKTRACE switch on/off stack backtracing.
        else if (eq (option, "BACKtrace")) {
          OPTION_BACKTRACE (&A68G_JOB) = A68G_TRUE;
        } else if (eq (option, "NOBACKtrace") || eq (option, "NO-BACKtrace")) {
          OPTION_BACKTRACE (&A68G_JOB) = A68G_FALSE;
        }
// BRACKETS extends Algol 68 syntax for brackets.
        else if (eq (option, "BRackets")) {
          OPTION_BRACKETS (&A68G_JOB) = A68G_TRUE;
        }
// BREAK switches on/off tracing of the running program.
        else if (eq (option, "BReakpoint")) {
          OPTION_NODEMASK (&A68G_JOB) |= BREAKPOINT_MASK;
        } else if (eq (option, "NOBReakpoint") || eq (option, "NO-BReakpoint")) {
          OPTION_NODEMASK (&A68G_JOB) &= ~BREAKPOINT_MASK;
        }
// CHECK and NORUN just check for syntax.
        else if (eq (option, "CHeck") || eq (option, "NORun") || eq (option, "NO-Run")) {
          OPTION_CHECK_ONLY (&A68G_JOB) = A68G_TRUE;
        }
// CLOCK times program execution.
        else if (eq (option, "CLock")) {
          OPTION_CLOCK (&A68G_JOB) = A68G_TRUE;
        }
// COMPILE switches on/off compilation.
        else if (eq (option, "Compile")) {
          #if defined (BUILD_UNIX)
            OPTION_COMPILE (&A68G_JOB) = A68G_TRUE;
            OPTION_COMPILE_CHECK (&A68G_JOB) = A68G_TRUE;
            if (OPTION_OPT_LEVEL (&A68G_JOB) < OPTIMISE_1) {
              OPTION_OPT_LEVEL (&A68G_JOB) = OPTIMISE_1;
            }
            OPTION_RUN_SCRIPT (&A68G_JOB) = A68G_FALSE;
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        } else if (eq (option, "NOCompile") || eq (option, "NO-Compile")) {
          #if defined (BUILD_UNIX)
            OPTION_COMPILE (&A68G_JOB) = A68G_FALSE;
            OPTION_RUN_SCRIPT (&A68G_JOB) = A68G_FALSE;
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        }
// ECHO echoes a text, is treated later.
        else if (eq (option, "ECHO")) {
          if (strchr (option, '=') == NO_TEXT) {
            FORWARD (i);
            if (i != NO_OPTION_LIST) {
              if (strcmp (STR (i), "=") == 0) {
                FORWARD (i);
              }
            }
          }
        }
// ERROR-CHECK generates (some) runtime checks for O2, O3, Ofast.
        else if (eq (option, "ERRor-check")) {
          OPTION_COMPILE_CHECK (&A68G_JOB) = A68G_TRUE;
        }
// EXECUTE and PRINT execute their argument as Algol 68 text.
        else if (eq (option, "Execute") || eq (option, "X") || eq (option, "Print")) {
          if (cmd_line == A68G_FALSE) {
            option_error (start_l, full_option, ERROR_COMMAND_LINE);
          } else if ((FORWARD (i)) != NO_OPTION_LIST) {
            BOOL_T error = A68G_FALSE;
            if (strcmp (STR (i), "=") == 0) {
              error = (BOOL_T) ((FORWARD (i)) == NO_OPTION_LIST);
            }
            if (!error) {
              BUFFER name, new_name;
              int s_errno = errno;
              a68g_bufcpy (name, HIDDEN_TEMP_FILE_NAME, BUFFER_SIZE);
              a68g_bufcat (name, ".a68", BUFFER_SIZE);
              FILE *f = a68g_fopen (name, "w", new_name);
              ABEND (f == NO_FILE, ERROR_ACTION, NO_TEXT);
              errno = s_errno;
              if (eq (option, "Execute") || eq (option, "X")) {
                fprintf (f, "(%s)\n", STR (i));
              } else {
                fprintf (f, "(print (((%s), new line)))\n", STR (i));
              }
              ASSERT (fclose (f) == 0);
              FILE_INITIAL_NAME (&A68G_JOB) = new_string (new_name, NO_TEXT);
            } else {
              option_error (start_l, full_option, ERROR_MISSING_STUFF);
            }
          } else {
            option_error (start_l, full_option, ERROR_MISSING_STUFF);
          }
        }
// EXIT stops option processing.
        else if (eq (option, "EXIT")) {
          siga = A68G_FALSE;
        }
// EXTENSIVE set of options for an extensive listing.
        else if (eq (option, "EXTensive")) {
          OPTION_SOURCE_LISTING (&A68G_JOB) = A68G_TRUE;
          OPTION_OBJECT_LISTING (&A68G_JOB) = A68G_TRUE;
          OPTION_TREE_LISTING (&A68G_JOB) = A68G_TRUE;
          OPTION_CROSS_REFERENCE (&A68G_JOB) = A68G_TRUE;
          OPTION_MOID_LISTING (&A68G_JOB) = A68G_TRUE;
          OPTION_STANDARD_PRELUDE_LISTING (&A68G_JOB) = A68G_TRUE;
          OPTION_STATISTICS_LISTING (&A68G_JOB) = A68G_TRUE;
          OPTION_UNUSED (&A68G_JOB) = A68G_TRUE;
          OPTION_NODEMASK (&A68G_JOB) |= (CROSS_REFERENCE_MASK | TREE_MASK | CODE_MASK | SOURCE_MASK);
        }
// FILE accepts its argument as filename.
        else if (eq (option, "File") && cmd_line) {
          FORWARD (i);
          if (i != NO_OPTION_LIST && strcmp (STR (i), "=") == 0) {
            FORWARD (i);
          }
          if (i != NO_OPTION_LIST) {
            if (!name_set) {
              FILE_INITIAL_NAME (&A68G_JOB) = new_string (STR (i), NO_TEXT);
              name_set = A68G_TRUE;
            } else {
              option_error (start_l, full_option, ERROR_MULTIPLE_SOURCE_FILES);
            }
          } else {
            option_error (start_l, full_option, ERROR_MISSING_STUFF);
          }
        }
// FOLD performs constant folding in basic lay-out formatting..
        else if (eq (option, "FOLD")) {
          OPTION_INDENT (&A68G_JOB) = A68G_TRUE;
          OPTION_FOLD (&A68G_JOB) = A68G_TRUE;
          OPTION_CHECK_ONLY (&A68G_JOB) = A68G_TRUE;
        }
// KEEP switches off/on object file deletion.
        else if (eq (option, "KEEP")) {
          OPTION_KEEP (&A68G_JOB) = A68G_TRUE;
        } else if (eq (option, "NOKEEP") || eq (option, "NO-KEEP")) {
          OPTION_KEEP (&A68G_JOB) = A68G_FALSE;
        }
// INCLUDE preprocessor item stops option processing.
        else if (eq (option, "INCLUDE") || eq (option, "READ")) {
          siga = A68G_FALSE;
        }
// LICENSE states the license
        else if (eq (option, "LICense")) {
          OPTION_LICENSE (&A68G_JOB) = A68G_TRUE;
        }
// LISTING set of options for a default listing.
        else if (eq (option, "Listing")) {
          OPTION_SOURCE_LISTING (&A68G_JOB) = A68G_TRUE;
          OPTION_CROSS_REFERENCE (&A68G_JOB) = A68G_TRUE;
          OPTION_STATISTICS_LISTING (&A68G_JOB) = A68G_TRUE;
          OPTION_NODEMASK (&A68G_JOB) |= (SOURCE_MASK | CROSS_REFERENCE_MASK);
        }
// MODULAR-ARITHMETIC. 
        else if (eq (option, "MODular-arithmetic")) {
        // Make A68G permissive towards BITS values corresponding to negative INT values that RR forbids.
          OPTION_NODEMASK (&A68G_JOB) |= MODULAR_MASK;
        } else if (eq (option, "NOMODular-arithmetic") || eq (option, "NO-MODular-arithmetic")) {
          OPTION_NODEMASK (&A68G_JOB) &= ~MODULAR_MASK;
        }
// MOIDS prints an overview of moids used in the program.
        else if (eq (option, "MOIDS")) {
          OPTION_MOID_LISTING (&A68G_JOB) = A68G_TRUE;
        }
// MONITOR or DEBUG invokes the debugger at runtime errors.
        else if (eq (option, "MONitor") || eq (option, "DEBUG")) {
          OPTION_DEBUG (&A68G_JOB) = A68G_TRUE;
        }
// NEED or LIBrary require the argument as environ.
        else if (eq (option, "NEED") || eq (option, "LIBrary")) {
          FORWARD (i);
          if (i == NO_OPTION_LIST) {
            option_error (start_l, full_option, ERROR_MISSING_STUFF);
          } else {
            OPTION_LIST_T *save = i; BOOL_T good = A68G_FALSE;
            do {
              good = need_library (i);
              if (good) {
                save = i;
                FORWARD (i);
              } else {
                i = save;
              }
            } while (good && i != NO_OPTION_LIST);
          }
        }
// NOTICES switches notices on/off.
        else if (eq (option, "Notices")) {
          OPTION_NO_NOTICES (&A68G_JOB) = A68G_FALSE;
        } else if (eq (option, "NONotices")) {
          OPTION_NO_NOTICES (&A68G_JOB) = A68G_TRUE;
        } else if (eq (option, "NO-NOTICEs")) {
          OPTION_NO_NOTICES (&A68G_JOB) = A68G_TRUE;
        }
 // NOGC is for a68g development purposes.
        else if (eq (option, "NOGC") || eq (option, "NO-GC")) {
          OPTION_CONSERVATIVE_GC (&A68G_JOB) = A68G_GC_HALT;
        }
// OBJECT prints object lines.
        else if (eq (option, "OBJECT")) {
          OPTION_OBJECT_LISTING (&A68G_JOB) = A68G_TRUE;
        } else if (eq (option, "NOOBJECT") || eq (option, "NO-OBJECT")) {
          OPTION_OBJECT_LISTING (&A68G_JOB) = A68G_FALSE;
        }
// OPTIMISE switches on/off optimisation.
        else if (eq (option, "OPTimise") || eq (option, "OPTimize")) {
          #if defined (BUILD_UNIX)
            OPTION_COMPILE_CHECK (&A68G_JOB) = A68G_TRUE;
            OPTION_OPT_LEVEL (&A68G_JOB) = OPTIMISE_1;
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        } else if (eq (option, "NOOptimize") || eq (option, "NO-Optimize")) {
          #if defined (BUILD_UNIX)
            OPTION_OPT_LEVEL (&A68G_JOB) = NO_OPTIMISE;
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        } else if (eq (option, "O0")) {
          #if defined (BUILD_UNIX)
            OPTION_OPT_LEVEL (&A68G_JOB) = NO_OPTIMISE;
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        } else if (eq (option, "OG")) {
          #if defined (BUILD_UNIX)
            OPTION_COMPILE_CHECK (&A68G_JOB) = A68G_TRUE;
            OPTION_OPT_LEVEL (&A68G_JOB) = OPTIMISE_0;
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        } else if (eq (option, "O") || eq (option, "O1")) {
          #if defined (BUILD_UNIX)
            OPTION_COMPILE_CHECK (&A68G_JOB) = A68G_TRUE;
            OPTION_OPT_LEVEL (&A68G_JOB) = OPTIMISE_1;
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        } else if (eq (option, "O2")) {
          #if defined (BUILD_UNIX)
            OPTION_COMPILE_CHECK (&A68G_JOB) = A68G_FALSE;
            OPTION_OPT_LEVEL (&A68G_JOB) = OPTIMISE_2;
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        } else if (eq (option, "O3")) {
          #if defined (BUILD_UNIX)
            OPTION_COMPILE_CHECK (&A68G_JOB) = A68G_FALSE;
            OPTION_OPT_LEVEL (&A68G_JOB) = OPTIMISE_3;
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        } else if (eq (option, "Ofast")) {
          #if defined (BUILD_UNIX)
            OPTION_COMPILE_CHECK (&A68G_JOB) = A68G_FALSE;
            OPTION_OPT_LEVEL (&A68G_JOB) = OPTIMISE_FAST;
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        }
// PEDANTIC switches portcheck and warnings on.
        else if (eq (option, "PEDANTIC")) {
          OPTION_PORTCHECK (&A68G_JOB) = A68G_TRUE;
          OPTION_NO_NOTICES (&A68G_JOB) = A68G_FALSE;
          OPTION_NO_WARNINGS (&A68G_JOB) = A68G_FALSE;
        }
// PORTCHECK switches portcheck on/off.
        else if (eq (option, "PORTcheck")) {
          OPTION_PORTCHECK (&A68G_JOB) = A68G_TRUE;
        }
        else if (eq (option, "NOPORTcheck")) {
          OPTION_PORTCHECK (&A68G_JOB) = A68G_FALSE;
        } else if (eq (option, "NO-PORTcheck")) {
          OPTION_PORTCHECK (&A68G_JOB) = A68G_FALSE;
        }
// PRAGMATS switches pragmat processing on/off.
        else if (eq (option, "PRagmats")) {
          OPTION_PRAGMAT_SEMA (&A68G_JOB) = A68G_TRUE;
        } else if (eq (option, "NOPRagmats") || eq (option, "NO-PRagmats")) {
          OPTION_PRAGMAT_SEMA (&A68G_JOB) = A68G_FALSE;
        }
// PRECISION sets LONG LONG precision.
        else if (eq (option, "PRECision")) {
          BOOL_T error = A68G_FALSE;
          INT_T N = fetch_integral (option, &i, &error, A68G_MAX_INT);
          int k = width_to_mp_digits (N);
          if (k <= 0 || error || errno > 0) {
            option_error (start_l, full_option, ERROR_INVALID_VALUE);
          } else if (long_mp_digits () > 0 && long_mp_digits () != k) {
            option_error (start_l, full_option, ERROR_PRECISION_SET);
          } else if (k > mp_digits ()) {
            set_long_mp_digits (k);
          } else {
            option_error (start_l, full_option, ERROR_PRECISION_TOO_LOW);
          }
        }
// PRELUDELISTING cross references preludes.
        else if (eq (option, "PRELUDElisting") || eq (option, "PRELUDE-listing")) {
          OPTION_SOURCE_LISTING (&A68G_JOB) = A68G_TRUE;
          OPTION_CROSS_REFERENCE (&A68G_JOB) = A68G_TRUE;
          OPTION_STATISTICS_LISTING (&A68G_JOB) = A68G_TRUE;
          OPTION_NODEMASK (&A68G_JOB) |= (SOURCE_MASK | CROSS_REFERENCE_MASK);
          OPTION_STANDARD_PRELUDE_LISTING (&A68G_JOB) = A68G_TRUE;
        }
// PREPROCESSOR stops option processing.
        else if (eq (option, "PREPROCESSOR") || eq (option, "NOPREPROCESSOR") || eq (option, "NO-PREPROCESSOR")) {
          siga = A68G_FALSE;
        } 
// PRETTY and INDENT perform basic pretty printing.
        else if (eq (option, "PRETty-print") || eq (option, "INDENT")) {
          OPTION_PRETTY (&A68G_JOB) = A68G_TRUE;
          OPTION_CHECK_ONLY (&A68G_JOB) = A68G_TRUE;
        }
// QUIET switches all warnings and notices off.
        else if (eq (option, "Quiet")) {
          OPTION_QUIET (&A68G_JOB) = A68G_TRUE;
          OPTION_NO_NOTICES (&A68G_JOB) = A68G_TRUE;
          OPTION_NO_WARNINGS (&A68G_JOB) = A68G_TRUE;
        }
// QUOTESTROPPING sets stropping to quote stropping.
        else if (eq (option, "QUOTEstropping")) {
          OPTION_STROPPING (&A68G_JOB) = QUOTE_STROPPING;
        } else if (eq (option, "QUOTE-stropping")) {
          OPTION_STROPPING (&A68G_JOB) = QUOTE_STROPPING;
        }
// REGRESSION sets preferences when running the test suite - undocumented option.
        else if (eq (option, "REGRession")) {
          OPTION_NO_NOTICES (&A68G_JOB) = A68G_FALSE;
          OPTION_NO_WARNINGS (&A68G_JOB) = A68G_FALSE;
          OPTION_PORTCHECK (&A68G_JOB) = A68G_TRUE;
          OPTION_REGRESSION_TEST (&A68G_JOB) = A68G_TRUE;
          OPTION_TIME_LIMIT (&A68G_JOB) = 300;
          OPTION_KEEP (&A68G_JOB) = A68G_TRUE;
          A68G (term_width) = MAX_TERM_WIDTH;
        }
// REDUCTIONS gives parser reductions.
        else if (eq (option, "REDuctions")) {
          OPTION_REDUCTIONS (&A68G_JOB) = A68G_TRUE;
        }
// RESTART restarts system calls at TIME-LIMIT check.
        else if (eq (option, "RESTART") || eq (option, "NORESTART") || eq (option, "NO-RESTART")) {
          #if defined (BUILD_UNIX)
            if (eq (option, "RESTART")) {
              OPTION_RESTART (&A68G_JOB) = A68G_TRUE;
            } else if (eq (option, "NORESTART") || eq (option, "NO-RESTART")) {
              OPTION_RESTART (&A68G_JOB) = A68G_FALSE;
            }
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        }
// RERUN re-uses an existing .so file.
        else if (eq (option, "RERUN")) {
          #if defined (BUILD_LINUX)
            OPTION_COMPILE (&A68G_JOB) = A68G_FALSE;
            OPTION_RERUN (&A68G_JOB) = A68G_TRUE;
            if (OPTION_OPT_LEVEL (&A68G_JOB) < OPTIMISE_1) {
              OPTION_OPT_LEVEL (&A68G_JOB) = OPTIMISE_1;
            }
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        }
// RUN overrides NORUN.
        else if (eq (option, "RUN")) {
          OPTION_RUN (&A68G_JOB) = A68G_TRUE;
        }
// RUN-QUOTE-SCRIPT runs a compiled .sh script.
        else if (eq (option, "RUN-QUOTE-SCRIPT")) {
          #if defined (BUILD_UNIX)
            FORWARD (i);
            if (i != NO_OPTION_LIST) {
              if (!name_set) {
                FILE_INITIAL_NAME (&A68G_JOB) = new_string (STR (i), NO_TEXT);
                name_set = A68G_TRUE;
              } else {
                option_error (start_l, full_option, ERROR_MULTIPLE_SOURCE_FILES);
              }
            } else {
              option_error (start_l, full_option, ERROR_MISSING_STUFF);
            }
            skip = A68G_TRUE;
            OPTION_RUN_SCRIPT (&A68G_JOB) = A68G_TRUE;
            OPTION_STROPPING (&A68G_JOB) = QUOTE_STROPPING;
            OPTION_COMPILE (&A68G_JOB) = A68G_FALSE;
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        }
// RUN-SCRIPT runs a compiled .sh script.
        else if (eq (option, "RUN-SCRIPT")) {
          #if defined (BUILD_UNIX)
            FORWARD (i);
            if (i != NO_OPTION_LIST) {
              if (!name_set) {
                FILE_INITIAL_NAME (&A68G_JOB) = new_string (STR (i), NO_TEXT);
                name_set = A68G_TRUE;
              } else {
                option_error (start_l, full_option, ERROR_MULTIPLE_SOURCE_FILES);
              }
            } else {
              option_error (start_l, full_option, ERROR_MISSING_STUFF);
            }
            skip = A68G_TRUE;
            OPTION_RUN_SCRIPT (&A68G_JOB) = A68G_TRUE;
            OPTION_NO_NOTICES (&A68G_JOB) = A68G_TRUE;
            OPTION_NO_WARNINGS (&A68G_JOB) = A68G_TRUE;
            OPTION_COMPILE (&A68G_JOB) = A68G_FALSE;
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        }
// SAFEGC is for a68g development purposes.
        else if (eq (option, "SAFEGC") || eq (option, "SAFE-GC")) {
          OPTION_CONSERVATIVE_GC (&A68G_JOB) = A68G_GC_SAFE;
        } 
// SCRIPT takes next argument as filename.
        else if (eq (option, "Script") && cmd_line) {
        // Further options on the command line are not processed, but stored.
          FORWARD (i);
          if (i != NO_OPTION_LIST) {
            if (!name_set) {
              FILE_INITIAL_NAME (&A68G_JOB) = new_string (STR (i), NO_TEXT);
              name_set = A68G_TRUE;
            } else {
              option_error (start_l, full_option, ERROR_MULTIPLE_SOURCE_FILES);
            }
          } else {
            option_error (start_l, full_option, ERROR_MISSING_STUFF);
          }
          skip = A68G_TRUE;
        }
// STATISTICS prints process statistics.
        else if (eq (option, "STatistics")) {
          OPTION_STATISTICS_LISTING (&A68G_JOB) = A68G_TRUE;
        }
// SOURCE prints source lines.
        else if (eq (option, "SOURCE")) {
          OPTION_SOURCE_LISTING (&A68G_JOB) = A68G_TRUE;
          OPTION_NODEMASK (&A68G_JOB) |= SOURCE_MASK;
        } else if (eq (option, "NOSOURCE") || eq (option, "NO-SOURCE")) {
          OPTION_NODEMASK (&A68G_JOB) &= ~SOURCE_MASK;
        }
// STORAGE, HEAP, HANDLES, STACK, FRAME and OVERHEAD set core allocation.
        else if (eq (option, "STOrage") || eq (option, "HEAP") || eq (option, "HANDLES") || eq (option, "STACK") || eq (option, "FRAME") || eq (option, "OVERHEAD")) {
          BOOL_T error = A68G_FALSE;
          INT_T k = fetch_integral (option, &i, &error, MAX_MEM_SIZE);
          if (error || errno > 0) {
            option_error (start_l, full_option, ERROR_INVALID_VALUE);
          } else if (k > 0) {
            if (eq (option, "STOrage")) {
              default_mem_sizes (k, start_l, full_option);
            } else {
              if (k < A68G (storage_overhead)) {
                option_error (start_l, full_option, ERROR_INVALID_VALUE);
                k = A68G (storage_overhead);
              }
              storage_limit (k + A68G (storage_overhead));
              if (eq (option, "HEAP")) {
                A68G (heap_size) = k;
              } else if (eq (option, "HANDLES")) {
                A68G (handle_pool_size) = k;
              } else if (eq (option, "STACK")) {
                A68G (expr_stack_size) = k;
              } else if (eq (option, "FRAME")) {
                A68G (frame_stack_size) = k;
              } else if (eq (option, "OVERHEAD")) {
                A68G (storage_overhead) = k;
              }
            }
          }
        }
// STRICT ignores A68G extensions to A68 syntax.
        else if (eq (option, "STRict")) {
          OPTION_STRICT (&A68G_JOB) = A68G_TRUE;
          OPTION_PORTCHECK (&A68G_JOB) = A68G_TRUE;
        }
// TECH prints out some tech stuff.
        else if (eq (option, "TECHnicalities")) {
          tech_stuff ();
        }
// TIMELIMIT lets the interpreter stop after so-many seconds.
        else if (eq (option, "TImelimit") || eq (option, "TIME-Limit")) {
          #if defined (BUILD_LINUX)
            BOOL_T error = A68G_FALSE;
            INT_T k = fetch_integral (option, &i, &error, A68G_MAX_INT);
            if (error || errno > 0) {
              option_error (start_l, full_option, ERROR_INVALID_VALUE);
            } else if (k < 1) {
              option_error (start_l, full_option, ERROR_INVALID_VALUE);
            } else {
              OPTION_TIME_LIMIT (&A68G_JOB) = k;
            }
          #else
            option_error (start_l, full_option, ERROR_PLATFORM);
          #endif
        }
// TREE switches on/off printing of the syntax tree. This gets bulky!.
        else if (eq (option, "TREE")) {
          OPTION_SOURCE_LISTING (&A68G_JOB) = A68G_TRUE;
          OPTION_TREE_LISTING (&A68G_JOB) = A68G_TRUE;
          OPTION_NODEMASK (&A68G_JOB) |= (TREE_MASK | SOURCE_MASK);
        } else if (eq (option, "NOTREE") || eq (option, "NO-TREE")) {
          OPTION_NODEMASK (&A68G_JOB) ^= (TREE_MASK | SOURCE_MASK);
        }
// TRACE switches on/off tracing of the running program.
        else if (eq (option, "TRace")) {
          OPTION_TRACE (&A68G_JOB) = A68G_TRUE;
          OPTION_NODEMASK (&A68G_JOB) |= BREAKPOINT_TRACE_MASK;
        } else if (eq (option, "NOTRace") || eq (option, "NO-TRace")) {
          OPTION_NODEMASK (&A68G_JOB) &= ~BREAKPOINT_TRACE_MASK;
        }
// UNUSED indicates unused tags.
        else if (eq (option, "UNUSED")) {
          OPTION_UNUSED (&A68G_JOB) = A68G_TRUE;
        }
// UPPERSTROPPING sets stropping to upper stropping, which is nowadays the expected default.
        else if (eq (option, "UPPERstropping") || eq (option, "UPPER-stropping")) {
          OPTION_STROPPING (&A68G_JOB) = UPPER_STROPPING;
        }
// VERBOSE in case you want to know what Algol68G is doing.
        else if (eq (option, "VERBose")) {
          OPTION_VERBOSE (&A68G_JOB) = A68G_TRUE;
        }
// VERIFY checks that argument is current a68g version number.
        else if (eq (option, "VERIFY")) {
          FORWARD (i);
          if (i != NO_OPTION_LIST && strcmp (STR (i), "=") == 0) {
            FORWARD (i);
          }
          if (i != NO_OPTION_LIST) {
            #if defined (BUILD_LINUX)
              ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%s verification \"%s\" does not match script verification \"%s\"", A68G (a68g_cmd_name), PACKAGE_STRING, STR (i)) >= 0);
              ABEND (strcmp (PACKAGE_STRING, STR (i)) != 0, new_string (A68G (output_line), NO_TEXT), "outdated script");
            #else
              option_error (start_l, full_option, ERROR_PLATFORM);
            #endif
          } else {
            option_error (start_l, full_option, ERROR_MISSING_STUFF);
          }
        }
// VERSION lists the current version at an appropriate time in the future.
        else if (eq (option, "Version")) {
          OPTION_VERSION (&A68G_JOB) = A68G_TRUE;
        } 
// WARNINGS switches unsuppressible warnings on/off.
        else if (eq (option, "Warnings")) {
          OPTION_NO_WARNINGS (&A68G_JOB) = A68G_FALSE;
        } else if (eq (option, "NOWarnings") || eq (option, "NO-Warnings")) {
          OPTION_NO_WARNINGS (&A68G_JOB) = A68G_TRUE;
        }
// XREF switch cross referencing on/off.
        else if (eq (option, "XREF")) {
          OPTION_SOURCE_LISTING (&A68G_JOB) = A68G_TRUE;
          OPTION_CROSS_REFERENCE (&A68G_JOB) = A68G_TRUE;
          OPTION_NODEMASK (&A68G_JOB) |= (CROSS_REFERENCE_MASK | SOURCE_MASK);
        } else if (eq (option, "NOXREF") || eq (option, "NO-Xref")) {
          OPTION_NODEMASK (&A68G_JOB) &= ~(CROSS_REFERENCE_MASK | SOURCE_MASK);
        }
// Unrecognised.
        else {
          option_error (start_l, full_option, ERROR_UNRECOGNISED_OPTION);
        }
      }
// Process next item, if present.
      if (i != NO_OPTION_LIST) {
        FORWARD (i);
      }
    }
  }
// Mark options as processed.
  for (; j != NO_OPTION_LIST; FORWARD (j)) {
    PROCESSED (j) = A68G_TRUE;
  }
  return (BOOL_T) (errno == 0);
}
