rts-transput.c

     
   1  //! @file rts-transput.c
   2  //! @author J. Marcel van der Veer
   3  
   4  //! @section Copyright
   5  //!
   6  //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
   7  //! Copyright 2001-2024 J. Marcel van der Veer [algol68g@xs4all.nl].
   8  
   9  //! @section License
  10  //!
  11  //! This program is free software; you can redistribute it and/or modify it 
  12  //! under the terms of the GNU General Public License as published by the 
  13  //! Free Software Foundation; either version 3 of the License, or 
  14  //! (at your option) any later version.
  15  //!
  16  //! This program is distributed in the hope that it will be useful, but 
  17  //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  18  //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
  19  //! more details. You should have received a copy of the GNU General Public 
  20  //! License along with this program. If not, see [http://www.gnu.org/licenses/].
  21  
  22  //! @section Synopsis 
  23  //!
  24  //! Transput routines.
  25  
  26  #include "a68g.h"
  27  #include "a68g-genie.h"
  28  #include "a68g-prelude.h"
  29  #include "a68g-transput.h"
  30  
  31  // Transput - General routines and unformatted transput.
  32  // But Eeyore wasn't listening. He was taking the balloon out, and putting
  33  // it back again, as happy as could be ... Winnie the Pooh, A.A. Milne.
  34  // - Revised Report on the Algorithmic Language Algol 68.
  35  
  36  // File table handling 
  37  // In a table we record opened files.
  38  // When execution ends, unclosed files are closed, and temps are removed.
  39  // This keeps /tmp free of spurious files :-)
  40  
  41  //! @brief Init a file entry.
  42  
  43  void init_file_entry (int k)
  44  {
  45    if (k >= 0 && k < MAX_OPEN_FILES) {
  46      FILE_ENTRY *fe = &(A68 (file_entries)[k]);
  47      POS (fe) = NO_NODE;
  48      IS_OPEN (fe) = A68_FALSE;
  49      IS_TMP (fe) = A68_FALSE;
  50      FD (fe) = A68_NO_FILE;
  51      IDF (fe) = nil_ref;
  52    }
  53  }
  54  
  55  //! @brief Initialise file entry table.
  56  
  57  void init_file_entries (void)
  58  {
  59    for (int k = 0; k < MAX_OPEN_FILES; k++) {
  60      init_file_entry (k);
  61    }
  62  }
  63  
  64  //! @brief Store file for later closing when not explicitly closed.
  65  
  66  int store_file_entry (NODE_T * p, FILE_T fd, char *idf, BOOL_T is_tmp)
  67  {
  68    for (int k = 0; k < MAX_OPEN_FILES; k++) {
  69      FILE_ENTRY *fe = &(A68 (file_entries)[k]);
  70      if (!IS_OPEN (fe)) {
  71        int len = 1 + (int) strlen (idf);
  72        POS (fe) = p;
  73        IS_OPEN (fe) = A68_TRUE;
  74        IS_TMP (fe) = is_tmp;
  75        FD (fe) = fd;
  76        IDF (fe) = heap_generator (p, M_C_STRING, len);
  77        BLOCK_GC_HANDLE (&(IDF (fe)));
  78        a68_bufcpy (DEREF (char, &IDF (fe)), idf, len);
  79        return k;
  80      }
  81    }
  82    diagnostic (A68_RUNTIME_ERROR, p, ERROR_TOO_MANY_OPEN_FILES);
  83    exit_genie (p, A68_RUNTIME_ERROR);
  84    return -1;
  85  }
  86  
  87  //! @brief Close file and delete temp file.
  88  
  89  void close_file_entry (NODE_T * p, int k)
  90  {
  91    if (k >= 0 && k < MAX_OPEN_FILES) {
  92      FILE_ENTRY *fe = &(A68 (file_entries)[k]);
  93      if (IS_OPEN (fe)) {
  94  // Close the file.
  95        if (FD (fe) != A68_NO_FILE && close (FD (fe)) == -1) {
  96          init_file_entry (k);
  97          diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_CLOSE);
  98          exit_genie (p, A68_RUNTIME_ERROR);
  99        }
 100        IS_OPEN (fe) = A68_FALSE;
 101      }
 102    }
 103  }
 104  
 105  //! @brief Close file and delete temp file.
 106  
 107  void free_file_entry (NODE_T * p, int k)
 108  {
 109    close_file_entry (p, k);
 110    if (k >= 0 && k < MAX_OPEN_FILES) {
 111      FILE_ENTRY *fe = &(A68 (file_entries)[k]);
 112      if (IS_OPEN (fe)) {
 113  // Attempt to remove a temp file, but ignore failure.
 114        if (FD (fe) != A68_NO_FILE && IS_TMP (fe)) {
 115          if (!IS_NIL (IDF (fe))) {
 116            char *filename;
 117            CHECK_INIT (p, INITIALISED (&(IDF (fe))), M_ROWS);
 118            filename = DEREF (char, &IDF (fe));
 119            if (filename != NO_TEXT) {
 120              (void) remove (filename);
 121            }
 122          }
 123        }
 124  // Restore the fields.
 125        if (!IS_NIL (IDF (fe))) {
 126          UNBLOCK_GC_HANDLE (&(IDF (fe)));
 127        }
 128        init_file_entry (k);
 129      }
 130    }
 131  }
 132  
 133  //! @brief Close all files and delete all temp files.
 134  
 135  void free_file_entries (void)
 136  {
 137    for (int k = 0; k < MAX_OPEN_FILES; k++) {
 138      free_file_entry (NO_NODE, k);
 139    }
 140  }
 141  
 142  // Strings in transput are of arbitrary size. For this, we have transput buffers.
 143  // A transput buffer is a REF STRUCT (INT size, index, STRING buffer).
 144  // It is in the heap, but cannot be gc'ed. If it is too small, we give up on
 145  // it and make a larger one.
 146  
 147  A68_REF ref_transput_buffer[MAX_TRANSPUT_BUFFER];
 148  
 149  //! @brief Set max number of chars in a transput buffer.
 150  
 151  void set_transput_buffer_size (int n, int size)
 152  {
 153    A68_INT *k = (A68_INT *) (ADDRESS (&ref_transput_buffer[n]));
 154    STATUS (k) = INIT_MASK;
 155    VALUE (k) = size;
 156  }
 157  
 158  //! @brief Set char index for transput buffer.
 159  
 160  void set_transput_buffer_index (int n, int cindex)
 161  {
 162    A68_INT *k = (A68_INT *) (ADDRESS (&ref_transput_buffer[n]) + SIZE (M_INT));
 163    STATUS (k) = INIT_MASK;
 164    VALUE (k) = cindex;
 165  }
 166  
 167  //! @brief Get max number of chars in a transput buffer.
 168  
 169  int get_transput_buffer_size (int n)
 170  {
 171    A68_INT *k = (A68_INT *) (ADDRESS (&ref_transput_buffer[n]));
 172    return VALUE (k);
 173  }
 174  
 175  //! @brief Get char index for transput buffer.
 176  
 177  int get_transput_buffer_index (int n)
 178  {
 179    A68_INT *k = (A68_INT *) (ADDRESS (&ref_transput_buffer[n]) + SIZE (M_INT));
 180    return VALUE (k);
 181  }
 182  
 183  //! @brief Get char[] from transput buffer.
 184  
 185  char *get_transput_buffer (int n)
 186  {
 187    return (char *) (ADDRESS (&ref_transput_buffer[n]) + 2 * SIZE (M_INT));
 188  }
 189  
 190  //! @brief Mark transput buffer as no longer in use.
 191  
 192  void unblock_transput_buffer (int n)
 193  {
 194    set_transput_buffer_index (n, -1);
 195  }
 196  
 197  //! @brief Find first unused transput buffer (for opening a file).
 198  
 199  int get_unblocked_transput_buffer (NODE_T * p)
 200  {
 201    for (int k = FIXED_TRANSPUT_BUFFERS; k < MAX_TRANSPUT_BUFFER; k++) {
 202      if (get_transput_buffer_index (k) == -1) {
 203        return k;
 204      }
 205    }
 206  // Oops!
 207    diagnostic (A68_RUNTIME_ERROR, p, ERROR_TOO_MANY_OPEN_FILES);
 208    exit_genie (p, A68_RUNTIME_ERROR);
 209    return -1;
 210  }
 211  
 212  //! @brief Empty contents of transput buffer.
 213  
 214  void reset_transput_buffer (int n)
 215  {
 216    set_transput_buffer_index (n, 0);
 217    (get_transput_buffer (n))[0] = NULL_CHAR;
 218  }
 219  
 220  //! @brief Initialise transput buffers before use.
 221  
 222  void init_transput_buffers (NODE_T * p)
 223  {
 224    for (int k = 0; k < MAX_TRANSPUT_BUFFER; k++) {
 225      ref_transput_buffer[k] = heap_generator (p, M_ROWS, 2 * SIZE (M_INT) + TRANSPUT_BUFFER_SIZE);
 226      BLOCK_GC_HANDLE (&ref_transput_buffer[k]);
 227      set_transput_buffer_size (k, TRANSPUT_BUFFER_SIZE);
 228      reset_transput_buffer (k);
 229    }
 230  // Last buffers are available for FILE values.
 231    for (int k = FIXED_TRANSPUT_BUFFERS; k < MAX_TRANSPUT_BUFFER; k++) {
 232      unblock_transput_buffer (k);
 233    }
 234  }
 235  
 236  //! @brief Make a transput buffer larger.
 237  
 238  void enlarge_transput_buffer (NODE_T * p, int k, int size)
 239  {
 240    int n = get_transput_buffer_index (k);
 241    char *sb_1 = get_transput_buffer (k), *sb_2;
 242    UNBLOCK_GC_HANDLE (&ref_transput_buffer[k]);
 243    ref_transput_buffer[k] = heap_generator (p, M_ROWS, 2 * SIZE (M_INT) + size);
 244    BLOCK_GC_HANDLE (&ref_transput_buffer[k]);
 245    set_transput_buffer_size (k, size);
 246    set_transput_buffer_index (k, n);
 247    sb_2 = get_transput_buffer (k);
 248    a68_bufcpy (sb_2, sb_1, size);
 249  }
 250  
 251  //! @brief Add char to transput buffer; if the buffer is full, make it larger.
 252  
 253  void plusab_transput_buffer (NODE_T * p, int k, char ch)
 254  {
 255    char *sb = get_transput_buffer (k);
 256    int size = get_transput_buffer_size (k);
 257    int n = get_transput_buffer_index (k);
 258    if (n == size - 2) {
 259      enlarge_transput_buffer (p, k, 10 * size);
 260      plusab_transput_buffer (p, k, ch);
 261    } else {
 262      sb[n] = ch;
 263      sb[n + 1] = NULL_CHAR;
 264      set_transput_buffer_index (k, n + 1);
 265    }
 266  }
 267  
 268  //! @brief Add char to transput buffer at the head; if the buffer is full, make it larger.
 269  
 270  void plusto_transput_buffer (NODE_T * p, char ch, int k)
 271  {
 272    char *sb = get_transput_buffer (k);
 273    int size = get_transput_buffer_size (k);
 274    int n = get_transput_buffer_index (k);
 275    if (n == size - 2) {
 276      enlarge_transput_buffer (p, k, 10 * size);
 277      plusto_transput_buffer (p, ch, k);
 278    } else {
 279      MOVE (&sb[1], &sb[0], (unt) size);
 280      sb[0] = ch;
 281      sb[n + 1] = NULL_CHAR;
 282      set_transput_buffer_index (k, n + 1);
 283    }
 284  }
 285  
 286  //! @brief Add chars to transput buffer.
 287  
 288  void add_chars_transput_buffer (NODE_T * p, int k, int N, char *ch)
 289  {
 290    for (int j = 0; j < N; j++) {
 291      plusab_transput_buffer (p, k, ch[j]);
 292    }
 293  }
 294  
 295  //! @brief Add char[] to transput buffer.
 296  
 297  void add_string_transput_buffer (NODE_T * p, int k, char *ch)
 298  {
 299    for (; ch[0] != NULL_CHAR; ch++) {
 300      plusab_transput_buffer (p, k, ch[0]);
 301    }
 302  }
 303  
 304  //! @brief Add A68 string to transput buffer.
 305  
 306  void add_a_string_transput_buffer (NODE_T * p, int k, BYTE_T * ref)
 307  {
 308    A68_REF row = *(A68_REF *) ref;
 309    CHECK_INIT (p, INITIALISED (&row), M_ROWS);
 310    A68_ARRAY *arr; A68_TUPLE *tup;
 311    GET_DESCRIPTOR (arr, tup, &row);
 312    if (ROW_SIZE (tup) > 0) {
 313      BYTE_T *base_address = DEREF (BYTE_T, &ARRAY (arr));
 314      for (int i = LWB (tup); i <= UPB (tup); i++) {
 315        int addr = INDEX_1_DIM (arr, tup, i);
 316        A68_CHAR *ch = (A68_CHAR *) & (base_address[addr]);
 317        CHECK_INIT (p, INITIALISED (ch), M_CHAR);
 318        plusab_transput_buffer (p, k, (char) VALUE (ch));
 319      }
 320    }
 321  }
 322  
 323  //! @brief Pop A68 string and add to buffer.
 324  
 325  void add_string_from_stack_transput_buffer (NODE_T * p, int k)
 326  {
 327    DECREMENT_STACK_POINTER (p, A68_REF_SIZE);
 328    add_a_string_transput_buffer (p, k, STACK_TOP);
 329  }
 330  
 331  //! @brief Pop first character from transput buffer.
 332  
 333  char pop_char_transput_buffer (int k)
 334  {
 335    char *sb = get_transput_buffer (k);
 336    int n = get_transput_buffer_index (k);
 337    if (n <= 0) {
 338      return NULL_CHAR;
 339    } else {
 340      char ch = sb[0];
 341      MOVE (&sb[0], &sb[1], n);
 342      set_transput_buffer_index (k, n - 1);
 343      return ch;
 344    }
 345  }
 346  
 347  //! @brief Add C string to A68 string.
 348  
 349  void add_c_string_to_a_string (NODE_T * p, A68_REF ref_str, char *s)
 350  {
 351    int len_2 = (int) strlen (s);
 352  // left part.
 353    CHECK_REF (p, ref_str, M_REF_STRING);
 354    A68_REF a = *DEREF (A68_REF, &ref_str);
 355    CHECK_INIT (p, INITIALISED (&a), M_STRING);
 356    A68_ARRAY *arr_1; A68_TUPLE *tup_1;
 357    GET_DESCRIPTOR (arr_1, tup_1, &a);
 358    int len_1 = ROW_SIZE (tup_1);
 359  // Sum string.
 360    A68_REF c = heap_generator (p, M_STRING, DESCRIPTOR_SIZE (1));
 361    A68_REF d = heap_generator_2 (p, M_STRING, len_1 + len_2, SIZE (M_CHAR));
 362  // Calculate again since garbage collector might have moved data.
 363  // Todo: GC should not move volatile data.
 364    GET_DESCRIPTOR (arr_1, tup_1, &a);
 365  // Make descriptor of new string.
 366    A68_ARRAY *arr_3; A68_TUPLE *tup_3;
 367    GET_DESCRIPTOR (arr_3, tup_3, &c);
 368    DIM (arr_3) = 1;
 369    MOID (arr_3) = M_CHAR;
 370    ELEM_SIZE (arr_3) = SIZE (M_CHAR);
 371    SLICE_OFFSET (arr_3) = 0;
 372    FIELD_OFFSET (arr_3) = 0;
 373    ARRAY (arr_3) = d;
 374    LWB (tup_3) = 1;
 375    UPB (tup_3) = len_1 + len_2;
 376    SHIFT (tup_3) = LWB (tup_3);
 377    SPAN (tup_3) = 1;
 378  // add strings.
 379    BYTE_T *b_1 = (ROW_SIZE (tup_1) > 0 ? DEREF (BYTE_T, &ARRAY (arr_1)) : NO_BYTE);
 380    BYTE_T *b_3 = DEREF (BYTE_T, &ARRAY (arr_3));
 381    int u = 0;
 382    for (int v = LWB (tup_1); v <= UPB (tup_1); v++) {
 383      MOVE ((BYTE_T *) & b_3[u], (BYTE_T *) & b_1[INDEX_1_DIM (arr_1, tup_1, v)], SIZE (M_CHAR));
 384      u += SIZE (M_CHAR);
 385    }
 386    for (int v = 0; v < len_2; v++) {
 387      A68_CHAR ch;
 388      STATUS (&ch) = INIT_MASK;
 389      VALUE (&ch) = s[v];
 390      MOVE ((BYTE_T *) & b_3[u], (BYTE_T *) & ch, SIZE (M_CHAR));
 391      u += SIZE (M_CHAR);
 392    }
 393    *DEREF (A68_REF, &ref_str) = c;
 394  }
 395  
 396  //! @brief Purge buffer for file.
 397  
 398  void write_purge_buffer (NODE_T * p, A68_REF ref_file, int k)
 399  {
 400    A68_FILE *file = FILE_DEREF (&ref_file);
 401    if (IS_NIL (STRING (file))) {
 402      if (!(FD (file) == A68_STDOUT && A68 (halt_typing))) {
 403        WRITE (FD (file), get_transput_buffer (k));
 404      }
 405    } else {
 406      add_c_string_to_a_string (p, STRING (file), get_transput_buffer (k));
 407    }
 408    reset_transput_buffer (k);
 409  }
 410  
 411  // Routines that involve the A68 expression stack.
 412  
 413  //! @brief Allocate a temporary string on the stack.
 414  
 415  char *stack_string (NODE_T * p, int size)
 416  {
 417    char *new_str = (char *) STACK_TOP;
 418    INCREMENT_STACK_POINTER (p, size);
 419    if (A68_SP > A68 (expr_stack_limit)) {
 420      diagnostic (A68_RUNTIME_ERROR, p, ERROR_STACK_OVERFLOW);
 421      exit_genie (p, A68_RUNTIME_ERROR);
 422    }
 423    FILL (new_str, NULL_CHAR, size);
 424    return new_str;
 425  }
 426  
 427  // Transput basic RTS routines.
 428  
 429  //! @brief REF FILE standin
 430  
 431  void genie_stand_in (NODE_T * p)
 432  {
 433    PUSH_REF (p, A68 (stand_in));
 434  }
 435  
 436  //! @brief REF FILE standout
 437  
 438  void genie_stand_out (NODE_T * p)
 439  {
 440    PUSH_REF (p, A68 (stand_out));
 441  }
 442  
 443  //! @brief REF FILE standback
 444  
 445  void genie_stand_back (NODE_T * p)
 446  {
 447    PUSH_REF (p, A68 (stand_back));
 448  }
 449  
 450  //! @brief REF FILE standerror
 451  
 452  void genie_stand_error (NODE_T * p)
 453  {
 454    PUSH_REF (p, A68 (stand_error));
 455  }
 456  
 457  //! @brief CHAR error char
 458  
 459  void genie_error_char (NODE_T * p)
 460  {
 461    PUSH_VALUE (p, ERROR_CHAR, A68_CHAR);
 462  }
 463  
 464  //! @brief CHAR exp char
 465  
 466  void genie_exp_char (NODE_T * p)
 467  {
 468    PUSH_VALUE (p, EXPONENT_CHAR, A68_CHAR);
 469  }
 470  
 471  //! @brief CHAR flip char
 472  
 473  void genie_flip_char (NODE_T * p)
 474  {
 475    PUSH_VALUE (p, FLIP_CHAR, A68_CHAR);
 476  }
 477  
 478  //! @brief CHAR flop char
 479  
 480  void genie_flop_char (NODE_T * p)
 481  {
 482    PUSH_VALUE (p, FLOP_CHAR, A68_CHAR);
 483  }
 484  
 485  //! @brief CHAR null char
 486  
 487  void genie_null_char (NODE_T * p)
 488  {
 489    PUSH_VALUE (p, NULL_CHAR, A68_CHAR);
 490  }
 491  
 492  //! @brief CHAR blank
 493  
 494  void genie_blank_char (NODE_T * p)
 495  {
 496    PUSH_VALUE (p, BLANK_CHAR, A68_CHAR);
 497  }
 498  
 499  //! @brief CHAR newline char
 500  
 501  void genie_newline_char (NODE_T * p)
 502  {
 503    PUSH_VALUE (p, NEWLINE_CHAR, A68_CHAR);
 504  }
 505  
 506  //! @brief CHAR formfeed char
 507  
 508  void genie_formfeed_char (NODE_T * p)
 509  {
 510    PUSH_VALUE (p, FORMFEED_CHAR, A68_CHAR);
 511  }
 512  
 513  //! @brief CHAR tab char
 514  
 515  void genie_tab_char (NODE_T * p)
 516  {
 517    PUSH_VALUE (p, TAB_CHAR, A68_CHAR);
 518  }
 519  
 520  //! @brief CHANNEL standin channel
 521  
 522  void genie_stand_in_channel (NODE_T * p)
 523  {
 524    PUSH_OBJECT (p, A68 (stand_in_channel), A68_CHANNEL);
 525  }
 526  
 527  //! @brief CHANNEL standout channel
 528  
 529  void genie_stand_out_channel (NODE_T * p)
 530  {
 531    PUSH_OBJECT (p, A68 (stand_out_channel), A68_CHANNEL);
 532  }
 533  
 534  //! @brief CHANNEL stand draw channel
 535  
 536  void genie_stand_draw_channel (NODE_T * p)
 537  {
 538    PUSH_OBJECT (p, A68 (stand_draw_channel), A68_CHANNEL);
 539  }
 540  
 541  //! @brief CHANNEL standback channel
 542  
 543  void genie_stand_back_channel (NODE_T * p)
 544  {
 545    PUSH_OBJECT (p, A68 (stand_back_channel), A68_CHANNEL);
 546  }
 547  
 548  //! @brief CHANNEL standerror channel
 549  
 550  void genie_stand_error_channel (NODE_T * p)
 551  {
 552    PUSH_OBJECT (p, A68 (stand_error_channel), A68_CHANNEL);
 553  }
 554  
 555  //! @brief PROC STRING program idf
 556  
 557  void genie_program_idf (NODE_T * p)
 558  {
 559    PUSH_REF (p, c_to_a_string (p, FILE_SOURCE_NAME (&A68_JOB), DEFAULT_WIDTH));
 560  }
 561  
 562  // FILE and CHANNEL initialisations.
 563  
 564  //! @brief Set_default_event_procedure.
 565  
 566  void set_default_event_procedure (A68_PROCEDURE * z)
 567  {
 568    STATUS (z) = INIT_MASK;
 569    NODE (&(BODY (z))) = NO_NODE;
 570    ENVIRON (z) = 0;
 571  }
 572  
 573  //! @brief Initialise channel.
 574  
 575  void init_channel (A68_CHANNEL * chan, BOOL_T r, BOOL_T s, BOOL_T g, BOOL_T p, BOOL_T b, BOOL_T d)
 576  {
 577    STATUS (chan) = INIT_MASK;
 578    RESET (chan) = r;
 579    SET (chan) = s;
 580    GET (chan) = g;
 581    PUT (chan) = p;
 582    BIN (chan) = b;
 583    DRAW (chan) = d;
 584    COMPRESS (chan) = A68_TRUE;
 585  }
 586  
 587  //! @brief Set default event handlers.
 588  
 589  void set_default_event_procedures (A68_FILE * f)
 590  {
 591    set_default_event_procedure (&(FILE_END_MENDED (f)));
 592    set_default_event_procedure (&(PAGE_END_MENDED (f)));
 593    set_default_event_procedure (&(LINE_END_MENDED (f)));
 594    set_default_event_procedure (&(VALUE_ERROR_MENDED (f)));
 595    set_default_event_procedure (&(OPEN_ERROR_MENDED (f)));
 596    set_default_event_procedure (&(TRANSPUT_ERROR_MENDED (f)));
 597    set_default_event_procedure (&(FORMAT_END_MENDED (f)));
 598    set_default_event_procedure (&(FORMAT_ERROR_MENDED (f)));
 599  }
 600  
 601  //! @brief Set up a REF FILE object.
 602  
 603  void init_file (NODE_T * p, A68_REF * ref_file, A68_CHANNEL c, FILE_T s, BOOL_T rm, BOOL_T wm, BOOL_T cm, char *env)
 604  {
 605    char *filename = (env == NO_TEXT ? NO_TEXT : getenv (env));
 606    *ref_file = heap_generator (p, M_REF_FILE, SIZE (M_FILE));
 607    BLOCK_GC_HANDLE (ref_file);
 608    A68_FILE *f = FILE_DEREF (ref_file);
 609    STATUS (f) = INIT_MASK;
 610    TERMINATOR (f) = nil_ref;
 611    CHANNEL (f) = c;
 612    if (filename != NO_TEXT && strlen (filename) > 0) {
 613      int len = 1 + (int) strlen (filename);
 614      IDENTIFICATION (f) = heap_generator (p, M_C_STRING, len);
 615      BLOCK_GC_HANDLE (&(IDENTIFICATION (f)));
 616      a68_bufcpy (DEREF (char, &IDENTIFICATION (f)), filename, len);
 617      FD (f) = A68_NO_FILE;
 618      READ_MOOD (f) = A68_FALSE;
 619      WRITE_MOOD (f) = A68_FALSE;
 620      CHAR_MOOD (f) = A68_FALSE;
 621      DRAW_MOOD (f) = A68_FALSE;
 622    } else {
 623      IDENTIFICATION (f) = nil_ref;
 624      FD (f) = s;
 625      READ_MOOD (f) = rm;
 626      WRITE_MOOD (f) = wm;
 627      CHAR_MOOD (f) = cm;
 628      DRAW_MOOD (f) = A68_FALSE;
 629    }
 630    TRANSPUT_BUFFER (f) = get_unblocked_transput_buffer (p);
 631    reset_transput_buffer (TRANSPUT_BUFFER (f));
 632    END_OF_FILE (f) = A68_FALSE;
 633    TMP_FILE (f) = A68_FALSE;
 634    OPENED (f) = A68_TRUE;
 635    OPEN_EXCLUSIVE (f) = A68_FALSE;
 636    FORMAT (f) = nil_format;
 637    STRING (f) = nil_ref;
 638    STRPOS (f) = 0;
 639    FILE_ENTRY (f) = -1;
 640    set_default_event_procedures (f);
 641  }
 642  
 643  //! @brief Initialise the transput RTL.
 644  
 645  void genie_init_transput (NODE_T * p)
 646  {
 647    init_transput_buffers (p);
 648  // Channels.
 649    init_channel (&(A68 (stand_in_channel)), A68_FALSE, A68_FALSE, A68_TRUE, A68_FALSE, A68_FALSE, A68_FALSE);
 650    init_channel (&(A68 (stand_out_channel)), A68_FALSE, A68_FALSE, A68_FALSE, A68_TRUE, A68_FALSE, A68_FALSE);
 651    init_channel (&(A68 (stand_back_channel)), A68_TRUE, A68_TRUE, A68_TRUE, A68_TRUE, A68_TRUE, A68_FALSE);
 652    init_channel (&(A68 (stand_error_channel)), A68_FALSE, A68_FALSE, A68_FALSE, A68_TRUE, A68_FALSE, A68_FALSE);
 653    init_channel (&(A68 (associate_channel)), A68_TRUE, A68_TRUE, A68_TRUE, A68_TRUE, A68_TRUE, A68_FALSE);
 654    init_channel (&(A68 (skip_channel)), A68_FALSE, A68_FALSE, A68_FALSE, A68_FALSE, A68_FALSE, A68_FALSE);
 655  #if defined (HAVE_GNU_PLOTUTILS)
 656    init_channel (&(A68 (stand_draw_channel)), A68_FALSE, A68_FALSE, A68_FALSE, A68_FALSE, A68_FALSE, A68_TRUE);
 657  #else
 658    init_channel (&(A68 (stand_draw_channel)), A68_FALSE, A68_FALSE, A68_FALSE, A68_FALSE, A68_FALSE, A68_TRUE);
 659  #endif
 660  // Files.
 661    init_file (p, &(A68 (stand_in)), A68 (stand_in_channel), A68_STDIN, A68_TRUE, A68_FALSE, A68_TRUE, "A68_STANDIN");
 662    init_file (p, &(A68 (stand_out)), A68 (stand_out_channel), A68_STDOUT, A68_FALSE, A68_TRUE, A68_TRUE, "A68_STANDOUT");
 663    init_file (p, &(A68 (stand_back)), A68 (stand_back_channel), A68_NO_FILE, A68_FALSE, A68_FALSE, A68_FALSE, NO_TEXT);
 664    init_file (p, &(A68 (stand_error)), A68 (stand_error_channel), A68_STDERR, A68_FALSE, A68_TRUE, A68_TRUE, "A68_STANDERROR");
 665    init_file (p, &(A68 (skip_file)), A68 (skip_channel), A68_NO_FILE, A68_FALSE, A68_FALSE, A68_FALSE, NO_TEXT);
 666  }
 667  
 668  //! @brief PROC (REF FILE) STRING idf
 669  
 670  void genie_idf (NODE_T * p)
 671  {
 672    A68_REF ref_file;
 673    POP_REF (p, &ref_file);
 674    CHECK_REF (p, ref_file, M_REF_FILE);
 675    ref_file = *(A68_REF *) STACK_TOP;
 676    A68_REF ref_filename = IDENTIFICATION (FILE_DEREF (&ref_file));
 677    CHECK_REF (p, ref_filename, M_ROWS);
 678    char *filename = DEREF (char, &ref_filename);
 679    PUSH_REF (p, c_to_a_string (p, filename, DEFAULT_WIDTH));
 680  }
 681  
 682  //! @brief PROC (REF FILE) STRING term
 683  
 684  void genie_term (NODE_T * p)
 685  {
 686    A68_REF ref_file;
 687    POP_REF (p, &ref_file);
 688    CHECK_REF (p, ref_file, M_REF_FILE);
 689    ref_file = *(A68_REF *) STACK_TOP;
 690    A68_REF ref_term = TERMINATOR (FILE_DEREF (&ref_file));
 691    CHECK_REF (p, ref_term, M_ROWS);
 692    char *term = DEREF (char, &ref_term);
 693    PUSH_REF (p, c_to_a_string (p, term, DEFAULT_WIDTH));
 694  }
 695  
 696  //! @brief PROC (REF FILE, STRING) VOID make term
 697  
 698  void genie_make_term (NODE_T * p)
 699  {
 700    A68_REF ref_file, str;
 701    POP_REF (p, &str);
 702    POP_REF (p, &ref_file);
 703    CHECK_REF (p, ref_file, M_REF_FILE);
 704    ref_file = *(A68_REF *) STACK_TOP;
 705    A68_FILE *file = FILE_DEREF (&ref_file);
 706  // Don't check initialisation so we can "make term" before opening.
 707    int size = a68_string_size (p, str);
 708    if (INITIALISED (&(TERMINATOR (file))) && !IS_NIL (TERMINATOR (file))) {
 709      UNBLOCK_GC_HANDLE (&(TERMINATOR (file)));
 710    }
 711    TERMINATOR (file) = heap_generator (p, M_C_STRING, 1 + size);
 712    BLOCK_GC_HANDLE (&(TERMINATOR (file)));
 713    ASSERT (a_to_c_string (p, DEREF (char, &TERMINATOR (file)), str) != NO_TEXT);
 714  }
 715  
 716  //! @brief PROC (REF FILE) BOOL put possible
 717  
 718  void genie_put_possible (NODE_T * p)
 719  {
 720    A68_REF ref_file;
 721    POP_REF (p, &ref_file);
 722    CHECK_REF (p, ref_file, M_REF_FILE);
 723    A68_FILE *file = FILE_DEREF (&ref_file);
 724    CHECK_INIT (p, INITIALISED (file), M_FILE);
 725    PUSH_VALUE (p, PUT (&CHANNEL (file)), A68_BOOL);
 726  }
 727  
 728  //! @brief PROC (REF FILE) BOOL get possible
 729  
 730  void genie_get_possible (NODE_T * p)
 731  {
 732    A68_REF ref_file;
 733    POP_REF (p, &ref_file);
 734    CHECK_REF (p, ref_file, M_REF_FILE);
 735    A68_FILE *file = FILE_DEREF (&ref_file);
 736    CHECK_INIT (p, INITIALISED (file), M_FILE);
 737    PUSH_VALUE (p, GET (&CHANNEL (file)), A68_BOOL);
 738  }
 739  
 740  //! @brief PROC (REF FILE) BOOL bin possible
 741  
 742  void genie_bin_possible (NODE_T * p)
 743  {
 744    A68_REF ref_file;
 745    POP_REF (p, &ref_file);
 746    CHECK_REF (p, ref_file, M_REF_FILE);
 747    A68_FILE *file = FILE_DEREF (&ref_file);
 748    CHECK_INIT (p, INITIALISED (file), M_FILE);
 749    PUSH_VALUE (p, BIN (&CHANNEL (file)), A68_BOOL);
 750  }
 751  
 752  //! @brief PROC (REF FILE) BOOL set possible
 753  
 754  void genie_set_possible (NODE_T * p)
 755  {
 756    A68_REF ref_file;
 757    POP_REF (p, &ref_file);
 758    CHECK_REF (p, ref_file, M_REF_FILE);
 759    A68_FILE *file = FILE_DEREF (&ref_file);
 760    CHECK_INIT (p, INITIALISED (file), M_FILE);
 761    PUSH_VALUE (p, SET (&CHANNEL (file)), A68_BOOL);
 762  }
 763  
 764  //! @brief PROC (REF FILE) BOOL reidf possible
 765  
 766  void genie_reidf_possible (NODE_T * p)
 767  {
 768    A68_REF ref_file;
 769    POP_REF (p, &ref_file);
 770    CHECK_REF (p, ref_file, M_REF_FILE);
 771    A68_FILE *file = FILE_DEREF (&ref_file);
 772    CHECK_INIT (p, INITIALISED (file), M_FILE);
 773    PUSH_VALUE (p, A68_FALSE, A68_BOOL);
 774  }
 775  
 776  //! @brief PROC (REF FILE) BOOL reset possible
 777  
 778  void genie_reset_possible (NODE_T * p)
 779  {
 780    A68_REF ref_file;
 781    POP_REF (p, &ref_file);
 782    CHECK_REF (p, ref_file, M_REF_FILE);
 783    A68_FILE *file = FILE_DEREF (&ref_file);
 784    CHECK_INIT (p, INITIALISED (file), M_FILE);
 785    PUSH_VALUE (p, RESET (&CHANNEL (file)), A68_BOOL);
 786  }
 787  
 788  //! @brief PROC (REF FILE) BOOL compressible
 789  
 790  void genie_compressible (NODE_T * p)
 791  {
 792    A68_REF ref_file;
 793    A68_FILE *file;
 794    POP_REF (p, &ref_file);
 795    CHECK_REF (p, ref_file, M_REF_FILE);
 796    file = FILE_DEREF (&ref_file);
 797    CHECK_INIT (p, INITIALISED (file), M_FILE);
 798    PUSH_VALUE (p, COMPRESS (&CHANNEL (file)), A68_BOOL);
 799  }
 800  
 801  //! @brief PROC (REF FILE) BOOL draw possible
 802  
 803  void genie_draw_possible (NODE_T * p)
 804  {
 805    A68_REF ref_file;
 806    POP_REF (p, &ref_file);
 807    CHECK_REF (p, ref_file, M_REF_FILE);
 808    A68_FILE *file = FILE_DEREF (&ref_file);
 809    CHECK_INIT (p, INITIALISED (file), M_FILE);
 810    PUSH_VALUE (p, DRAW (&CHANNEL (file)), A68_BOOL);
 811  }
 812  
 813  //! @brief PROC (REF FILE, STRING, CHANNEL) INT open
 814  
 815  void genie_open (NODE_T * p)
 816  {
 817    A68_CHANNEL channel;
 818    POP_OBJECT (p, &channel, A68_CHANNEL);
 819    A68_REF ref_iden;
 820    POP_REF (p, &ref_iden);
 821    CHECK_REF (p, ref_iden, M_REF_STRING);
 822    A68_REF ref_file;
 823    POP_REF (p, &ref_file);
 824    CHECK_REF (p, ref_file, M_REF_FILE);
 825    A68_FILE *file = FILE_DEREF (&ref_file);
 826    STATUS (file) = INIT_MASK;
 827    FILE_ENTRY (file) = -1;
 828    CHANNEL (file) = channel;
 829    OPENED (file) = A68_TRUE;
 830    OPEN_EXCLUSIVE (file) = A68_FALSE;
 831    READ_MOOD (file) = A68_FALSE;
 832    WRITE_MOOD (file) = A68_FALSE;
 833    CHAR_MOOD (file) = A68_FALSE;
 834    DRAW_MOOD (file) = A68_FALSE;
 835    TMP_FILE (file) = A68_FALSE;
 836    int size = a68_string_size (p, ref_iden);
 837    if (INITIALISED (&(IDENTIFICATION (file))) && !IS_NIL (IDENTIFICATION (file))) {
 838      UNBLOCK_GC_HANDLE (&(IDENTIFICATION (file)));
 839    }
 840    IDENTIFICATION (file) = heap_generator (p, M_C_STRING, 1 + size);
 841    BLOCK_GC_HANDLE (&(IDENTIFICATION (file)));
 842    ASSERT (a_to_c_string (p, DEREF (char, &IDENTIFICATION (file)), ref_iden) != NO_TEXT);
 843    TERMINATOR (file) = nil_ref;
 844    FORMAT (file) = nil_format;
 845    FD (file) = A68_NO_FILE;
 846    if (INITIALISED (&(STRING (file))) && !IS_NIL (STRING (file))) {
 847      UNBLOCK_GC_HANDLE (DEREF (A68_REF, &STRING (file)));
 848    }
 849    STRING (file) = nil_ref;
 850    STRPOS (file) = 0;
 851    DEVICE_MADE (&DEVICE (file)) = A68_FALSE;
 852    STREAM (&DEVICE (file)) = NO_STREAM;
 853    set_default_event_procedures (file);
 854    {
 855      struct stat status;
 856      if (stat (DEREF (char, &IDENTIFICATION (file)), &status) == 0) {
 857        PUSH_VALUE (p, (S_ISREG (ST_MODE (&status)) != 0 ? 0 : 1), A68_INT);
 858      } else {
 859        PUSH_VALUE (p, 1, A68_INT);
 860      }
 861      errno = 0;
 862    }
 863  }
 864  
 865  //! @brief PROC (REF FILE, STRING, CHANNEL) INT establish
 866  
 867  void genie_establish (NODE_T * p)
 868  {
 869    A68_CHANNEL channel;
 870    POP_OBJECT (p, &channel, A68_CHANNEL);
 871    A68_REF ref_iden;
 872    POP_REF (p, &ref_iden);
 873    CHECK_REF (p, ref_iden, M_REF_STRING);
 874    A68_REF ref_file;
 875    POP_REF (p, &ref_file);
 876    CHECK_REF (p, ref_file, M_REF_FILE);
 877    A68_FILE *file = FILE_DEREF (&ref_file);
 878    STATUS (file) = INIT_MASK;
 879    FILE_ENTRY (file) = -1;
 880    CHANNEL (file) = channel;
 881    OPENED (file) = A68_TRUE;
 882    OPEN_EXCLUSIVE (file) = A68_TRUE;
 883    READ_MOOD (file) = A68_FALSE;
 884    WRITE_MOOD (file) = A68_FALSE;
 885    CHAR_MOOD (file) = A68_FALSE;
 886    DRAW_MOOD (file) = A68_FALSE;
 887    TMP_FILE (file) = A68_FALSE;
 888    if (!PUT (&CHANNEL (file))) {
 889      diagnostic (A68_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "putting");
 890      exit_genie (p, A68_RUNTIME_ERROR);
 891    }
 892    int size = a68_string_size (p, ref_iden);
 893    if (INITIALISED (&(IDENTIFICATION (file))) && !IS_NIL (IDENTIFICATION (file))) {
 894      UNBLOCK_GC_HANDLE (&(IDENTIFICATION (file)));
 895    }
 896    IDENTIFICATION (file) = heap_generator (p, M_C_STRING, 1 + size);
 897    BLOCK_GC_HANDLE (&(IDENTIFICATION (file)));
 898    ASSERT (a_to_c_string (p, DEREF (char, &IDENTIFICATION (file)), ref_iden) != NO_TEXT);
 899    TERMINATOR (file) = nil_ref;
 900    FORMAT (file) = nil_format;
 901    FD (file) = A68_NO_FILE;
 902    if (INITIALISED (&(STRING (file))) && !IS_NIL (STRING (file))) {
 903      UNBLOCK_GC_HANDLE (DEREF (A68_REF, &STRING (file)));
 904    }
 905    STRING (file) = nil_ref;
 906    STRPOS (file) = 0;
 907    DEVICE_MADE (&DEVICE (file)) = A68_FALSE;
 908    STREAM (&DEVICE (file)) = NO_STREAM;
 909    set_default_event_procedures (file);
 910    PUSH_VALUE (p, 0, A68_INT);
 911  }
 912  
 913  //! @brief PROC (REF FILE, CHANNEL) INT create
 914  
 915  void genie_create (NODE_T * p)
 916  {
 917    A68_CHANNEL channel;
 918    POP_OBJECT (p, &channel, A68_CHANNEL);
 919    A68_REF ref_file;
 920    POP_REF (p, &ref_file);
 921    CHECK_REF (p, ref_file, M_REF_FILE);
 922    A68_FILE *file = FILE_DEREF (&ref_file);
 923    STATUS (file) = INIT_MASK;
 924    FILE_ENTRY (file) = -1;
 925    CHANNEL (file) = channel;
 926    OPENED (file) = A68_TRUE;
 927    OPEN_EXCLUSIVE (file) = A68_FALSE;
 928    READ_MOOD (file) = A68_FALSE;
 929    WRITE_MOOD (file) = A68_FALSE;
 930    CHAR_MOOD (file) = A68_FALSE;
 931    DRAW_MOOD (file) = A68_FALSE;
 932    TMP_FILE (file) = A68_TRUE;
 933    if (INITIALISED (&(IDENTIFICATION (file))) && !IS_NIL (IDENTIFICATION (file))) {
 934      UNBLOCK_GC_HANDLE (&(IDENTIFICATION (file)));
 935    }
 936    IDENTIFICATION (file) = nil_ref;
 937    TERMINATOR (file) = nil_ref;
 938    FORMAT (file) = nil_format;
 939    FD (file) = A68_NO_FILE;
 940    if (INITIALISED (&(STRING (file))) && !IS_NIL (STRING (file))) {
 941      UNBLOCK_GC_HANDLE (DEREF (A68_REF, &STRING (file)));
 942    }
 943    STRING (file) = nil_ref;
 944    STRPOS (file) = 0;
 945    DEVICE_MADE (&DEVICE (file)) = A68_FALSE;
 946    STREAM (&DEVICE (file)) = NO_STREAM;
 947    set_default_event_procedures (file);
 948    PUSH_VALUE (p, 0, A68_INT);
 949  }
 950  
 951  //! @brief PROC (REF FILE, REF STRING) VOID associate
 952  
 953  void genie_associate (NODE_T * p)
 954  {
 955    A68_REF ref_string;
 956    POP_REF (p, &ref_string);
 957    CHECK_REF (p, ref_string, M_REF_STRING);
 958    A68_REF ref_file;
 959    POP_REF (p, &ref_file);
 960    CHECK_REF (p, ref_file, M_REF_FILE);
 961    if (IS_IN_HEAP (&ref_file) && !IS_IN_HEAP (&ref_string)) {
 962      diagnostic (A68_RUNTIME_ERROR, p, ERROR_SCOPE_DYNAMIC_1, M_REF_STRING);
 963      exit_genie (p, A68_RUNTIME_ERROR);
 964    } else if (IS_IN_FRAME (&ref_file) && IS_IN_FRAME (&ref_string)) {
 965      if (REF_SCOPE (&ref_string) > REF_SCOPE (&ref_file)) {
 966        diagnostic (A68_RUNTIME_ERROR, p, ERROR_SCOPE_DYNAMIC_1, M_REF_STRING);
 967        exit_genie (p, A68_RUNTIME_ERROR);
 968      }
 969    }
 970    A68_FILE *file = FILE_DEREF (&ref_file);
 971    STATUS (file) = INIT_MASK;
 972    FILE_ENTRY (file) = -1;
 973    CHANNEL (file) = A68 (associate_channel);
 974    OPENED (file) = A68_TRUE;
 975    OPEN_EXCLUSIVE (file) = A68_FALSE;
 976    READ_MOOD (file) = A68_FALSE;
 977    WRITE_MOOD (file) = A68_FALSE;
 978    CHAR_MOOD (file) = A68_FALSE;
 979    DRAW_MOOD (file) = A68_FALSE;
 980    TMP_FILE (file) = A68_FALSE;
 981    if (INITIALISED (&(IDENTIFICATION (file))) && !IS_NIL (IDENTIFICATION (file))) {
 982      UNBLOCK_GC_HANDLE (&(IDENTIFICATION (file)));
 983    }
 984    IDENTIFICATION (file) = nil_ref;
 985    TERMINATOR (file) = nil_ref;
 986    FORMAT (file) = nil_format;
 987    FD (file) = A68_NO_FILE;
 988    if (INITIALISED (&(STRING (file))) && !IS_NIL (STRING (file))) {
 989      UNBLOCK_GC_HANDLE (DEREF (A68_REF, &STRING (file)));
 990    }
 991    STRING (file) = ref_string;
 992    BLOCK_GC_HANDLE ((A68_REF *) (&(STRING (file))));
 993    STRPOS (file) = 0;
 994    DEVICE_MADE (&DEVICE (file)) = A68_FALSE;
 995    STREAM (&DEVICE (file)) = NO_STREAM;
 996    set_default_event_procedures (file);
 997  }
 998  
 999  //! @brief PROC (REF FILE) VOID close
1000  
1001  void genie_close (NODE_T * p)
1002  {
1003    A68_REF ref_file;
1004    POP_REF (p, &ref_file);
1005    CHECK_REF (p, ref_file, M_REF_FILE);
1006    A68_FILE *file = FILE_DEREF (&ref_file);
1007    CHECK_INIT (p, INITIALISED (file), M_FILE);
1008    if (!OPENED (file) || (!READ_MOOD (file) && !WRITE_MOOD (file) && !DRAW_MOOD (file))) {
1009      return;
1010    }
1011    DEVICE_MADE (&DEVICE (file)) = A68_FALSE;
1012  #if defined (HAVE_GNU_PLOTUTILS)
1013    if (DEVICE_OPENED (&DEVICE (file))) {
1014      ASSERT (close_device (p, file) == A68_TRUE);
1015      STREAM (&DEVICE (file)) = NO_STREAM;
1016      return;
1017    }
1018  #endif
1019    FD (file) = A68_NO_FILE;
1020    OPENED (file) = A68_FALSE;
1021    unblock_transput_buffer (TRANSPUT_BUFFER (file));
1022    set_default_event_procedures (file);
1023    free_file_entry (p, FILE_ENTRY (file));
1024  }
1025  
1026  //! @brief PROC (REF FILE) VOID lock
1027  
1028  void genie_lock (NODE_T * p)
1029  {
1030    A68_REF ref_file;
1031    POP_REF (p, &ref_file);
1032    CHECK_REF (p, ref_file, M_REF_FILE);
1033    A68_FILE *file = FILE_DEREF (&ref_file);
1034    CHECK_INIT (p, INITIALISED (file), M_FILE);
1035    if (!OPENED (file) || (!READ_MOOD (file) && !WRITE_MOOD (file) && !DRAW_MOOD (file))) {
1036      return;
1037    }
1038    DEVICE_MADE (&DEVICE (file)) = A68_FALSE;
1039  #if defined (HAVE_GNU_PLOTUTILS)
1040    if (DEVICE_OPENED (&DEVICE (file))) {
1041      ASSERT (close_device (p, file) == A68_TRUE);
1042      STREAM (&DEVICE (file)) = NO_STREAM;
1043      return;
1044    }
1045  #endif
1046  #if defined (BUILD_UNIX)
1047    errno = 0;
1048    ASSERT (fchmod (FD (file), (mode_t) 0x0) != -1);
1049  #endif
1050    if (FD (file) != A68_NO_FILE && close (FD (file)) == -1) {
1051      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_LOCK);
1052      exit_genie (p, A68_RUNTIME_ERROR);
1053    } else {
1054      FD (file) = A68_NO_FILE;
1055      OPENED (file) = A68_FALSE;
1056      unblock_transput_buffer (TRANSPUT_BUFFER (file));
1057      set_default_event_procedures (file);
1058    }
1059    free_file_entry (p, FILE_ENTRY (file));
1060  }
1061  
1062  //! @brief PROC (REF FILE) VOID erase
1063  
1064  void genie_erase (NODE_T * p)
1065  {
1066    A68_REF ref_file;
1067    POP_REF (p, &ref_file);
1068    CHECK_REF (p, ref_file, M_REF_FILE);
1069    A68_FILE *file = FILE_DEREF (&ref_file);
1070    CHECK_INIT (p, INITIALISED (file), M_FILE);
1071    if (!OPENED (file) || (!READ_MOOD (file) && !WRITE_MOOD (file) && !DRAW_MOOD (file))) {
1072      return;
1073    }
1074    DEVICE_MADE (&DEVICE (file)) = A68_FALSE;
1075  #if defined (HAVE_GNU_PLOTUTILS)
1076    if (DEVICE_OPENED (&DEVICE (file))) {
1077      ASSERT (close_device (p, file) == A68_TRUE);
1078      STREAM (&DEVICE (file)) = NO_STREAM;
1079      return;
1080    }
1081  #endif
1082    if (FD (file) != A68_NO_FILE && close (FD (file)) == -1) {
1083      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_SCRATCH);
1084      exit_genie (p, A68_RUNTIME_ERROR);
1085    } else {
1086      unblock_transput_buffer (TRANSPUT_BUFFER (file));
1087      set_default_event_procedures (file);
1088    }
1089  // Remove the file.
1090    if (!IS_NIL (IDENTIFICATION (file))) {
1091      char *filename;
1092      CHECK_INIT (p, INITIALISED (&(IDENTIFICATION (file))), M_ROWS);
1093      filename = DEREF (char, &IDENTIFICATION (file));
1094      if (remove (filename) != 0) {
1095        diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_SCRATCH);
1096        exit_genie (p, A68_RUNTIME_ERROR);
1097      }
1098      UNBLOCK_GC_HANDLE (&(IDENTIFICATION (file)));
1099      IDENTIFICATION (file) = nil_ref;
1100    }
1101    init_file_entry (FILE_ENTRY (file));
1102  }
1103  
1104  //! @brief PROC (REF FILE) VOID backspace
1105  
1106  void genie_backspace (NODE_T * p)
1107  {
1108    ADDR_T pop_sp = A68_SP;
1109    PUSH_VALUE (p, -1, A68_INT);
1110    genie_set (p);
1111    A68_SP = pop_sp;
1112  }
1113  
1114  //! @brief PROC (REF FILE, INT) INT set
1115  
1116  void genie_set (NODE_T * p)
1117  {
1118    A68_INT pos;
1119    POP_OBJECT (p, &pos, A68_INT);
1120    A68_REF ref_file;
1121    POP_REF (p, &ref_file);
1122    CHECK_REF (p, ref_file, M_REF_FILE);
1123    A68_FILE *file = FILE_DEREF (&ref_file);
1124    CHECK_INIT (p, INITIALISED (file), M_FILE);
1125    if (!OPENED (file)) {
1126      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
1127      exit_genie (p, A68_RUNTIME_ERROR);
1128    }
1129    if (!SET (&CHANNEL (file))) {
1130      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_CANT_SET);
1131      exit_genie (p, A68_RUNTIME_ERROR);
1132    }
1133    if (!IS_NIL (STRING (file))) {
1134      A68_REF z = *DEREF (A68_REF, &STRING (file));
1135      int size;
1136  // Circumvent buffering problems.
1137      STRPOS (file) -= get_transput_buffer_index (TRANSPUT_BUFFER (file));
1138      ASSERT (STRPOS (file) > 0);
1139      reset_transput_buffer (TRANSPUT_BUFFER (file));
1140  // Now set.
1141      CHECK_INT_ADDITION (p, STRPOS (file), VALUE (&pos));
1142      STRPOS (file) += VALUE (&pos);
1143      A68_ARRAY *arr; A68_TUPLE *tup;
1144      GET_DESCRIPTOR (arr, tup, &z);
1145      size = ROW_SIZE (tup);
1146      if (size <= 0 || STRPOS (file) < 0 || STRPOS (file) >= size) {
1147        A68_BOOL res;
1148        on_event_handler (p, FILE_END_MENDED (FILE_DEREF (&ref_file)), ref_file);
1149        POP_OBJECT (p, &res, A68_BOOL);
1150        if (VALUE (&res) == A68_FALSE) {
1151          diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_ENDED);
1152          exit_genie (p, A68_RUNTIME_ERROR);
1153        }
1154      }
1155      PUSH_VALUE (p, STRPOS (file), A68_INT);
1156    } else if (FD (file) == A68_NO_FILE) {
1157      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_RESET);
1158      exit_genie (p, A68_RUNTIME_ERROR);
1159    } else {
1160      errno = 0;
1161      __off_t curpos = lseek (FD (file), 0, SEEK_CUR);
1162      __off_t maxpos = lseek (FD (file), 0, SEEK_END);
1163  // Circumvent buffering problems.
1164      int reserve = get_transput_buffer_index (TRANSPUT_BUFFER (file));
1165      curpos -= (__off_t) reserve;
1166      __off_t res = lseek (FD (file), -reserve, SEEK_CUR);
1167      ASSERT (res != -1 && errno == 0);
1168      reset_transput_buffer (TRANSPUT_BUFFER (file));
1169  // Now set.
1170      CHECK_INT_ADDITION (p, curpos, VALUE (&pos));
1171      curpos += VALUE (&pos);
1172      if (curpos < 0 || curpos >= maxpos) {
1173        A68_BOOL ret;
1174        on_event_handler (p, FILE_END_MENDED (FILE_DEREF (&ref_file)), ref_file);
1175        POP_OBJECT (p, &ret, A68_BOOL);
1176        if (VALUE (&ret) == A68_FALSE) {
1177          diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_ENDED);
1178          exit_genie (p, A68_RUNTIME_ERROR);
1179        }
1180        PUSH_VALUE (p, (int) lseek (FD (file), 0, SEEK_CUR), A68_INT);
1181      } else {
1182        res = lseek (FD (file), curpos, SEEK_SET);
1183        if (res == -1 || errno != 0) {
1184          diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_SET);
1185          exit_genie (p, A68_RUNTIME_ERROR);
1186        }
1187        PUSH_VALUE (p, (int) res, A68_INT);
1188      }
1189    }
1190  }
1191  
1192  //! @brief PROC (REF FILE) VOID reset
1193  
1194  void genie_reset (NODE_T * p)
1195  {
1196    A68_REF ref_file;
1197    POP_REF (p, &ref_file);
1198    CHECK_REF (p, ref_file, M_REF_FILE);
1199    A68_FILE *file = FILE_DEREF (&ref_file);
1200    CHECK_INIT (p, INITIALISED (file), M_FILE);
1201    if (!OPENED (file)) {
1202      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
1203      exit_genie (p, A68_RUNTIME_ERROR);
1204    }
1205    if (!RESET (&CHANNEL (file))) {
1206      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_CANT_RESET);
1207      exit_genie (p, A68_RUNTIME_ERROR);
1208    }
1209    if (IS_NIL (STRING (file))) {
1210      close_file_entry (p, FILE_ENTRY (file));
1211    } else {
1212      STRPOS (file) = 0;
1213    }
1214    READ_MOOD (file) = A68_FALSE;
1215    WRITE_MOOD (file) = A68_FALSE;
1216    CHAR_MOOD (file) = A68_FALSE;
1217    DRAW_MOOD (file) = A68_FALSE;
1218    FD (file) = A68_NO_FILE;
1219  //  set_default_event_procedures (file);.
1220  }
1221  
1222  //! @brief PROC (REF FILE, PROC (REF FILE) BOOL) VOID on file end
1223  
1224  void genie_on_file_end (NODE_T * p)
1225  {
1226    A68_PROCEDURE z;
1227    POP_PROCEDURE (p, &z);
1228    A68_REF ref_file;
1229    POP_REF (p, &ref_file);
1230    CHECK_REF (p, ref_file, M_REF_FILE);
1231    A68_FILE *file = FILE_DEREF (&ref_file);
1232    CHECK_INIT (p, INITIALISED (file), M_FILE);
1233    FILE_END_MENDED (file) = z;
1234  }
1235  
1236  //! @brief PROC (REF FILE, PROC (REF FILE) BOOL) VOID on page end
1237  
1238  void genie_on_page_end (NODE_T * p)
1239  {
1240    A68_PROCEDURE z;
1241    POP_PROCEDURE (p, &z);
1242    A68_REF ref_file;
1243    POP_REF (p, &ref_file);
1244    CHECK_REF (p, ref_file, M_REF_FILE);
1245    A68_FILE *file = FILE_DEREF (&ref_file);
1246    CHECK_INIT (p, INITIALISED (file), M_FILE);
1247    PAGE_END_MENDED (file) = z;
1248  }
1249  
1250  //! @brief PROC (REF FILE, PROC (REF FILE) BOOL) VOID on line end
1251  
1252  void genie_on_line_end (NODE_T * p)
1253  {
1254    A68_PROCEDURE z;
1255    POP_PROCEDURE (p, &z);
1256    A68_REF ref_file;
1257    POP_REF (p, &ref_file);
1258    CHECK_REF (p, ref_file, M_REF_FILE);
1259    A68_FILE *file = FILE_DEREF (&ref_file);
1260    CHECK_INIT (p, INITIALISED (file), M_FILE);
1261    LINE_END_MENDED (file) = z;
1262  }
1263  
1264  //! @brief PROC (REF FILE, PROC (REF FILE) BOOL) VOID on format end
1265  
1266  void genie_on_format_end (NODE_T * p)
1267  {
1268    A68_PROCEDURE z;
1269    POP_PROCEDURE (p, &z);
1270    A68_REF ref_file;
1271    POP_REF (p, &ref_file);
1272    CHECK_REF (p, ref_file, M_REF_FILE);
1273    A68_FILE *file = FILE_DEREF (&ref_file);
1274    CHECK_INIT (p, INITIALISED (file), M_FILE);
1275    FORMAT_END_MENDED (file) = z;
1276  }
1277  
1278  //! @brief PROC (REF FILE, PROC (REF FILE) BOOL) VOID on format error
1279  
1280  void genie_on_format_error (NODE_T * p)
1281  {
1282    A68_PROCEDURE z;
1283    POP_PROCEDURE (p, &z);
1284    A68_REF ref_file;
1285    POP_REF (p, &ref_file);
1286    CHECK_REF (p, ref_file, M_REF_FILE);
1287    A68_FILE *file = FILE_DEREF (&ref_file);
1288    CHECK_INIT (p, INITIALISED (file), M_FILE);
1289    FORMAT_ERROR_MENDED (file) = z;
1290  }
1291  
1292  //! @brief PROC (REF FILE, PROC (REF FILE) BOOL) VOID on value error
1293  
1294  void genie_on_value_error (NODE_T * p)
1295  {
1296    A68_PROCEDURE z;
1297    POP_PROCEDURE (p, &z);
1298    A68_REF ref_file;
1299    POP_REF (p, &ref_file);
1300    CHECK_REF (p, ref_file, M_REF_FILE);
1301    A68_FILE *file = FILE_DEREF (&ref_file);
1302    CHECK_INIT (p, INITIALISED (file), M_FILE);
1303    VALUE_ERROR_MENDED (file) = z;
1304  }
1305  
1306  //! @brief PROC (REF FILE, PROC (REF FILE) BOOL) VOID on open error
1307  
1308  void genie_on_open_error (NODE_T * p)
1309  {
1310    A68_PROCEDURE z;
1311    POP_PROCEDURE (p, &z);
1312    A68_REF ref_file;
1313    POP_REF (p, &ref_file);
1314    CHECK_REF (p, ref_file, M_REF_FILE);
1315    A68_FILE *file = FILE_DEREF (&ref_file);
1316    CHECK_INIT (p, INITIALISED (file), M_FILE);
1317    OPEN_ERROR_MENDED (file) = z;
1318  }
1319  
1320  //! @brief PROC (REF FILE, PROC (REF FILE) BOOL) VOID on transput error
1321  
1322  void genie_on_transput_error (NODE_T * p)
1323  {
1324    A68_PROCEDURE z;
1325    POP_PROCEDURE (p, &z);
1326    A68_REF ref_file;
1327    POP_REF (p, &ref_file);
1328    CHECK_REF (p, ref_file, M_REF_FILE);
1329    A68_FILE *file = FILE_DEREF (&ref_file);
1330    CHECK_INIT (p, INITIALISED (file), M_FILE);
1331    TRANSPUT_ERROR_MENDED (file) = z;
1332  }
1333  
1334  //! @brief Invoke event routine.
1335  
1336  void on_event_handler (NODE_T * p, A68_PROCEDURE z, A68_REF ref_file)
1337  {
1338    if (NODE (&(BODY (&z))) == NO_NODE) {
1339  // Default procedure.
1340      PUSH_VALUE (p, A68_FALSE, A68_BOOL);
1341    } else {
1342      ADDR_T pop_sp = A68_SP, pop_fp = A68_FP;
1343      PUSH_REF (p, ref_file);
1344      genie_call_event_routine (p, M_PROC_REF_FILE_BOOL, &z, pop_sp, pop_fp);
1345    }
1346  }
1347  
1348  //! @brief Handle end-of-file event.
1349  
1350  void end_of_file_error (NODE_T * p, A68_REF ref_file)
1351  {
1352    on_event_handler (p, FILE_END_MENDED (FILE_DEREF (&ref_file)), ref_file);
1353    A68_BOOL z;
1354    POP_OBJECT (p, &z, A68_BOOL);
1355    if (VALUE (&z) == A68_FALSE) {
1356      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_ENDED);
1357      exit_genie (p, A68_RUNTIME_ERROR);
1358    }
1359  }
1360  
1361  //! @brief Handle file-open-error event.
1362  
1363  void open_error (NODE_T * p, A68_REF ref_file, char *mode)
1364  {
1365    on_event_handler (p, OPEN_ERROR_MENDED (FILE_DEREF (&ref_file)), ref_file);
1366    A68_BOOL z;
1367    POP_OBJECT (p, &z, A68_BOOL);
1368    if (VALUE (&z) == A68_FALSE) {
1369      CHECK_REF (p, ref_file, M_REF_FILE);
1370      A68_FILE *file = FILE_DEREF (&ref_file);
1371      CHECK_INIT (p, INITIALISED (file), M_FILE);
1372      char *filename;
1373      if (!IS_NIL (IDENTIFICATION (file))) {
1374        filename = DEREF (char, &IDENTIFICATION (FILE_DEREF (&ref_file)));
1375      } else {
1376        filename = "(missing filename)";
1377      }
1378      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_CANNOT_OPEN_FOR, filename, mode);
1379      exit_genie (p, A68_RUNTIME_ERROR);
1380    }
1381  }
1382  
1383  //! @brief Handle value error event.
1384  
1385  void value_error (NODE_T * p, MOID_T * m, A68_REF ref_file)
1386  {
1387    A68_FILE *f = FILE_DEREF (&ref_file);
1388    if (END_OF_FILE (f)) {
1389      end_of_file_error (p, ref_file);
1390    } else {
1391      on_event_handler (p, VALUE_ERROR_MENDED (f), ref_file);
1392      A68_BOOL z;
1393      POP_OBJECT (p, &z, A68_BOOL);
1394      if (VALUE (&z) == A68_FALSE) {
1395        diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_TRANSPUT, m);
1396        exit_genie (p, A68_RUNTIME_ERROR);
1397      }
1398    }
1399  }
1400  
1401  //! @brief Handle value_error event.
1402  
1403  void value_sign_error (NODE_T * p, MOID_T * m, A68_REF ref_file)
1404  {
1405    A68_FILE *f = FILE_DEREF (&ref_file);
1406    if (END_OF_FILE (f)) {
1407      end_of_file_error (p, ref_file);
1408    } else {
1409      on_event_handler (p, VALUE_ERROR_MENDED (f), ref_file);
1410      A68_BOOL z;
1411      POP_OBJECT (p, &z, A68_BOOL);
1412      if (VALUE (&z) == A68_FALSE) {
1413        diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_TRANSPUT_SIGN, m);
1414        exit_genie (p, A68_RUNTIME_ERROR);
1415      }
1416    }
1417  }
1418  
1419  //! @brief Handle transput-error event.
1420  
1421  void transput_error (NODE_T * p, A68_REF ref_file, MOID_T * m)
1422  {
1423    on_event_handler (p, TRANSPUT_ERROR_MENDED (FILE_DEREF (&ref_file)), ref_file);
1424    A68_BOOL z;
1425    POP_OBJECT (p, &z, A68_BOOL);
1426    if (VALUE (&z) == A68_FALSE) {
1427      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_TRANSPUT, m);
1428      exit_genie (p, A68_RUNTIME_ERROR);
1429    }
1430  }
1431  
1432  // Implementation of put and get.
1433  
1434  //! @brief Get next char from file.
1435  
1436  int char_scanner (A68_FILE * f)
1437  {
1438    if (get_transput_buffer_index (TRANSPUT_BUFFER (f)) > 0) {
1439  // There are buffered characters.
1440      END_OF_FILE (f) = A68_FALSE;
1441      return pop_char_transput_buffer (TRANSPUT_BUFFER (f));
1442    } else if (IS_NIL (STRING (f))) {
1443  // Fetch next CHAR from the FILE.
1444      char ch;
1445      ssize_t chars_read = io_read_conv (FD (f), &ch, 1);
1446      if (chars_read == 1) {
1447        END_OF_FILE (f) = A68_FALSE;
1448        return ch;
1449      } else {
1450        END_OF_FILE (f) = A68_TRUE;
1451        return EOF_CHAR;
1452      }
1453    } else {
1454  // File is associated with a STRING. Give next CHAR. 
1455  // When we're outside the STRING give EOF_CHAR. 
1456      A68_REF z = *DEREF (A68_REF, &STRING (f)); A68_ARRAY *arr; A68_TUPLE *tup;
1457      GET_DESCRIPTOR (arr, tup, &z);
1458      int k = STRPOS (f) + LWB (tup);
1459      if (ROW_SIZE (tup) <= 0 || k < LWB (tup) || k > UPB (tup)) {
1460        END_OF_FILE (f) = A68_TRUE;
1461        return EOF_CHAR;
1462      } else {
1463        BYTE_T *base = DEREF (BYTE_T, &ARRAY (arr));
1464        A68_CHAR *ch = (A68_CHAR *) & (base[INDEX_1_DIM (arr, tup, k)]);
1465        STRPOS (f)++;
1466        return VALUE (ch);
1467      }
1468    }
1469  }
1470  
1471  //! @brief Push back look-ahead character to file.
1472  
1473  void unchar_scanner (NODE_T * p, A68_FILE * f, char ch)
1474  {
1475    END_OF_FILE (f) = A68_FALSE;
1476    plusab_transput_buffer (p, TRANSPUT_BUFFER (f), ch);
1477  }
1478  
1479  //! @brief PROC (REF FILE) BOOL eof
1480  
1481  void genie_eof (NODE_T * p)
1482  {
1483    A68_REF ref_file;
1484    POP_REF (p, &ref_file);
1485    CHECK_REF (p, ref_file, M_REF_FILE);
1486    A68_FILE *file = FILE_DEREF (&ref_file);
1487    CHECK_INIT (p, INITIALISED (file), M_FILE);
1488    if (!OPENED (file)) {
1489      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
1490      exit_genie (p, A68_RUNTIME_ERROR);
1491    }
1492    if (DRAW_MOOD (file)) {
1493      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
1494      exit_genie (p, A68_RUNTIME_ERROR);
1495    }
1496    if (WRITE_MOOD (file)) {
1497      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "write");
1498      exit_genie (p, A68_RUNTIME_ERROR);
1499    } else if (READ_MOOD (file)) {
1500      int ch = char_scanner (file);
1501      PUSH_VALUE (p, (BOOL_T) ((ch == EOF_CHAR || END_OF_FILE (file)) ? A68_TRUE : A68_FALSE), A68_BOOL);
1502      unchar_scanner (p, file, (char) ch);
1503    } else {
1504      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "undetermined");
1505      exit_genie (p, A68_RUNTIME_ERROR);
1506    }
1507  }
1508  
1509  //! @brief PROC (REF FILE) BOOL eoln
1510  
1511  void genie_eoln (NODE_T * p)
1512  {
1513    A68_REF ref_file;
1514    POP_REF (p, &ref_file);
1515    CHECK_REF (p, ref_file, M_REF_FILE);
1516    A68_FILE *file = FILE_DEREF (&ref_file);
1517    CHECK_INIT (p, INITIALISED (file), M_FILE);
1518    if (!OPENED (file)) {
1519      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
1520      exit_genie (p, A68_RUNTIME_ERROR);
1521    }
1522    if (DRAW_MOOD (file)) {
1523      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
1524      exit_genie (p, A68_RUNTIME_ERROR);
1525    }
1526    if (WRITE_MOOD (file)) {
1527      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "write");
1528      exit_genie (p, A68_RUNTIME_ERROR);
1529    } else if (READ_MOOD (file)) {
1530      int ch = char_scanner (file);
1531      if (END_OF_FILE (file)) {
1532        end_of_file_error (p, ref_file);
1533      }
1534      PUSH_VALUE (p, (BOOL_T) (ch == NEWLINE_CHAR ? A68_TRUE : A68_FALSE), A68_BOOL);
1535      unchar_scanner (p, file, (char) ch);
1536    } else {
1537      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "undetermined");
1538      exit_genie (p, A68_RUNTIME_ERROR);
1539    }
1540  }
1541  
1542  //! @brief PROC (REF FILE) VOID new line
1543  
1544  void genie_new_line (NODE_T * p)
1545  {
1546    A68_REF ref_file;
1547    POP_REF (p, &ref_file);
1548    CHECK_REF (p, ref_file, M_REF_FILE);
1549    A68_FILE *file = FILE_DEREF (&ref_file);
1550    CHECK_INIT (p, INITIALISED (file), M_FILE);
1551    if (!OPENED (file)) {
1552      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
1553      exit_genie (p, A68_RUNTIME_ERROR);
1554    }
1555    if (DRAW_MOOD (file)) {
1556      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
1557      exit_genie (p, A68_RUNTIME_ERROR);
1558    }
1559    if (WRITE_MOOD (file)) {
1560      on_event_handler (p, LINE_END_MENDED (file), ref_file);
1561      if (IS_NIL (STRING (file))) {
1562        WRITE (FD (file), NEWLINE_STRING);
1563      } else {
1564        add_c_string_to_a_string (p, STRING (file), NEWLINE_STRING);
1565      }
1566    } else if (READ_MOOD (file)) {
1567      BOOL_T siga = A68_TRUE;
1568      while (siga) {
1569        int ch;
1570        if (END_OF_FILE (file)) {
1571          end_of_file_error (p, ref_file);
1572        }
1573        ch = char_scanner (file);
1574        siga = (BOOL_T) ((ch != NEWLINE_CHAR) && (ch != EOF_CHAR) && !END_OF_FILE (file));
1575      }
1576    } else {
1577      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "undetermined");
1578      exit_genie (p, A68_RUNTIME_ERROR);
1579    }
1580  }
1581  
1582  //! @brief PROC (REF FILE) VOID new page
1583  
1584  void genie_new_page (NODE_T * p)
1585  {
1586    A68_REF ref_file;
1587    POP_REF (p, &ref_file);
1588    CHECK_REF (p, ref_file, M_REF_FILE);
1589    A68_FILE *file = FILE_DEREF (&ref_file);
1590    CHECK_INIT (p, INITIALISED (file), M_FILE);
1591    if (!OPENED (file)) {
1592      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
1593      exit_genie (p, A68_RUNTIME_ERROR);
1594    }
1595    if (DRAW_MOOD (file)) {
1596      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
1597      exit_genie (p, A68_RUNTIME_ERROR);
1598    }
1599    if (WRITE_MOOD (file)) {
1600      on_event_handler (p, PAGE_END_MENDED (file), ref_file);
1601      if (IS_NIL (STRING (file))) {
1602        WRITE (FD (file), "\f");
1603      } else {
1604        add_c_string_to_a_string (p, STRING (file), "\f");
1605      }
1606    } else if (READ_MOOD (file)) {
1607      BOOL_T siga = A68_TRUE;
1608      while (siga) {
1609        if (END_OF_FILE (file)) {
1610          end_of_file_error (p, ref_file);
1611        }
1612        int ch = char_scanner (file);
1613        siga = (BOOL_T) ((ch != FORMFEED_CHAR) && (ch != EOF_CHAR) && !END_OF_FILE (file));
1614      }
1615    } else {
1616      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "undetermined");
1617      exit_genie (p, A68_RUNTIME_ERROR);
1618    }
1619  }
1620  
1621  //! @brief PROC (REF FILE) VOID space
1622  
1623  void genie_space (NODE_T * p)
1624  {
1625    A68_REF ref_file;
1626    POP_REF (p, &ref_file);
1627    CHECK_REF (p, ref_file, M_REF_FILE);
1628    A68_FILE *file = FILE_DEREF (&ref_file);
1629    CHECK_INIT (p, INITIALISED (file), M_FILE);
1630    if (!OPENED (file)) {
1631      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
1632      exit_genie (p, A68_RUNTIME_ERROR);
1633    }
1634    if (DRAW_MOOD (file)) {
1635      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
1636      exit_genie (p, A68_RUNTIME_ERROR);
1637    }
1638    if (WRITE_MOOD (file)) {
1639      WRITE (FD (file), " ");
1640    } else if (READ_MOOD (file)) {
1641      if (!END_OF_FILE (file)) {
1642        (void) char_scanner (file);
1643      }
1644    } else {
1645      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "undetermined");
1646      exit_genie (p, A68_RUNTIME_ERROR);
1647    }
1648  }