rts-unformatted.c

     
   1  //! @file rts-unformatted.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-2025 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  //! Unformatted transput.
  25  
  26  #include "a68g.h"
  27  #include "a68g-genie.h"
  28  #include "a68g-prelude.h"
  29  #include "a68g-mp.h"
  30  #include "a68g-double.h"
  31  #include "a68g-transput.h"
  32  
  33  //! @brief Skip new-lines and form-feeds.
  34  
  35  void skip_nl_ff (NODE_T * p, int *ch, A68G_REF ref_file)
  36  {
  37    A68G_FILE *f = FILE_DEREF (&ref_file);
  38    while ((*ch) != EOF_CHAR && IS_NL_FF (*ch)) {
  39      A68G_BOOL *z = (A68G_BOOL *) STACK_TOP;
  40      ADDR_T pop_sp = A68G_SP;
  41      unchar_scanner (p, f, (char) (*ch));
  42      if (*ch == NEWLINE_CHAR) {
  43        on_event_handler (p, LINE_END_MENDED (f), ref_file);
  44        A68G_SP = pop_sp;
  45        if (VALUE (z) == A68G_FALSE) {
  46          PUSH_REF (p, ref_file);
  47          genie_new_line (p);
  48        }
  49      } else if (*ch == FORMFEED_CHAR) {
  50        on_event_handler (p, PAGE_END_MENDED (f), ref_file);
  51        A68G_SP = pop_sp;
  52        if (VALUE (z) == A68G_FALSE) {
  53          PUSH_REF (p, ref_file);
  54          genie_new_page (p);
  55        }
  56      }
  57      (*ch) = char_scanner (f);
  58    }
  59  }
  60  
  61  //! @brief Scan an int from file.
  62  
  63  void scan_integer (NODE_T * p, A68G_REF ref_file)
  64  {
  65    A68G_FILE *f = FILE_DEREF (&ref_file);
  66    reset_transput_buffer (INPUT_BUFFER);
  67    int ch = char_scanner (f);
  68    while (ch != EOF_CHAR && (IS_SPACE (ch) || IS_NL_FF (ch))) {
  69      if (IS_NL_FF (ch)) {
  70        skip_nl_ff (p, &ch, ref_file);
  71      } else {
  72        ch = char_scanner (f);
  73      }
  74    }
  75    if (ch != EOF_CHAR && (ch == '+' || ch == '-')) {
  76      plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
  77      ch = char_scanner (f);
  78    }
  79    while (ch != EOF_CHAR && IS_DIGIT (ch)) {
  80      plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
  81      ch = char_scanner (f);
  82    }
  83    if (ch != EOF_CHAR) {
  84      unchar_scanner (p, f, (char) ch);
  85    }
  86  }
  87  
  88  //! @brief Scan a real from file.
  89  
  90  void scan_real (NODE_T * p, A68G_REF ref_file)
  91  {
  92    A68G_FILE *f = FILE_DEREF (&ref_file);
  93    char x_e = EXPONENT_CHAR;
  94    reset_transput_buffer (INPUT_BUFFER);
  95    int ch = char_scanner (f);
  96    while (ch != EOF_CHAR && (IS_SPACE (ch) || IS_NL_FF (ch))) {
  97      if (IS_NL_FF (ch)) {
  98        skip_nl_ff (p, &ch, ref_file);
  99      } else {
 100        ch = char_scanner (f);
 101      }
 102    }
 103    if (ch != EOF_CHAR && (ch == '+' || ch == '-')) {
 104      plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
 105      ch = char_scanner (f);
 106    }
 107    while (ch != EOF_CHAR && IS_DIGIT (ch)) {
 108      plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
 109      ch = char_scanner (f);
 110    }
 111    if (ch == EOF_CHAR || !(ch == POINT_CHAR || TO_UPPER (ch) == TO_UPPER (x_e))) {
 112      goto salida;
 113    }
 114    if (ch == POINT_CHAR) {
 115      plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
 116      ch = char_scanner (f);
 117      while (ch != EOF_CHAR && IS_DIGIT (ch)) {
 118        plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
 119        ch = char_scanner (f);
 120      }
 121    }
 122    if (ch == EOF_CHAR || TO_UPPER (ch) != TO_UPPER (x_e)) {
 123      goto salida;
 124    }
 125    if (TO_UPPER (ch) == TO_UPPER (x_e)) {
 126      plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
 127      ch = char_scanner (f);
 128      while (ch != EOF_CHAR && ch == BLANK_CHAR) {
 129        ch = char_scanner (f);
 130      }
 131      if (ch != EOF_CHAR && (ch == '+' || ch == '-')) {
 132        plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
 133        ch = char_scanner (f);
 134      }
 135      while (ch != EOF_CHAR && IS_DIGIT (ch)) {
 136        plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
 137        ch = char_scanner (f);
 138      }
 139    }
 140  salida:if (ch != EOF_CHAR) {
 141      unchar_scanner (p, f, (char) ch);
 142    }
 143  }
 144  
 145  //! @brief Scan a bits from file.
 146  
 147  void scan_bits (NODE_T * p, A68G_REF ref_file)
 148  {
 149    A68G_FILE *f = FILE_DEREF (&ref_file);
 150    reset_transput_buffer (INPUT_BUFFER);
 151    int ch = char_scanner (f);
 152    while (ch != EOF_CHAR && (IS_SPACE (ch) || IS_NL_FF (ch))) {
 153      if (IS_NL_FF (ch)) {
 154        skip_nl_ff (p, &ch, ref_file);
 155      } else {
 156        ch = char_scanner (f);
 157      }
 158    }
 159    while (ch != EOF_CHAR && (ch == FLIP_CHAR || ch == FLOP_CHAR)) {
 160      plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
 161      ch = char_scanner (f);
 162    }
 163    if (ch != EOF_CHAR) {
 164      unchar_scanner (p, f, (char) ch);
 165    }
 166  }
 167  
 168  //! @brief Scan a char from file.
 169  
 170  void scan_char (NODE_T * p, A68G_REF ref_file)
 171  {
 172    A68G_FILE *f = FILE_DEREF (&ref_file);
 173    reset_transput_buffer (INPUT_BUFFER);
 174    int ch = char_scanner (f);
 175    skip_nl_ff (p, &ch, ref_file);
 176    if (ch != EOF_CHAR) {
 177      plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
 178    }
 179  }
 180  
 181  //! @brief Scan a string from file.
 182  
 183  void scan_string (NODE_T * p, char *term, A68G_REF ref_file)
 184  {
 185    A68G_FILE *f = FILE_DEREF (&ref_file);
 186    if (END_OF_FILE (f)) {
 187      reset_transput_buffer (INPUT_BUFFER);
 188      end_of_file_error (p, ref_file);
 189    } else {
 190      reset_transput_buffer (INPUT_BUFFER);
 191      int ch = char_scanner (f);
 192      BOOL_T siga = A68G_TRUE;
 193      while (siga) {
 194        if (ch == EOF_CHAR || END_OF_FILE (f)) {
 195          if (get_transput_buffer_index (INPUT_BUFFER) == 0) {
 196            end_of_file_error (p, ref_file);
 197          }
 198          siga = A68G_FALSE;
 199        } else if (IS_NL_FF (ch)) {
 200          ADDR_T pop_sp = A68G_SP;
 201          unchar_scanner (p, f, (char) ch);
 202          if (ch == NEWLINE_CHAR) {
 203            on_event_handler (p, LINE_END_MENDED (f), ref_file);
 204          } else if (ch == FORMFEED_CHAR) {
 205            on_event_handler (p, PAGE_END_MENDED (f), ref_file);
 206          }
 207          A68G_SP = pop_sp;
 208          siga = A68G_FALSE;
 209        } else if (term != NO_TEXT && strchr (term, ch) != NO_TEXT) {
 210          siga = A68G_FALSE;
 211          unchar_scanner (p, f, (char) ch);
 212        } else {
 213          plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
 214          ch = char_scanner (f);
 215        }
 216      }
 217    }
 218  }
 219  
 220  //! @brief Make temp file name.
 221  
 222  BOOL_T a68g_mkstemp (char *fn, int flags, mode_t permissions)
 223  {
 224  // "tmpnam" is not safe, "mkstemp" is Unix, so a68g brings its own tmpnam.
 225  #define TMP_SIZE 32
 226  #define TRIALS 32
 227    BUFFER tfilename;
 228    char *letters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
 229    size_t len = strlen (letters);
 230    BOOL_T good_file = A68G_FALSE;
 231  // Next are prefixes to try.
 232  // First we try /tmp, and if that won't go, the current dir.
 233    char *prefix[] = { "/tmp/a68g_", "./a68g_", NO_TEXT };
 234    for (int i = 0; prefix[i] != NO_TEXT; i++) {
 235      for (int k = 0; k < TRIALS && good_file == A68G_FALSE; k++) {
 236        a68g_bufcpy (tfilename, prefix[i], BUFFER_SIZE);
 237        for (int j = 0; j < TMP_SIZE; j++) {
 238          int cindex;
 239          do {
 240            cindex = (int) (a68g_unif_rand () * len);
 241          } while (cindex < 0 || cindex >= len);
 242          char chars[2];
 243          chars[0] = letters[cindex];
 244          chars[1] = NULL_CHAR;
 245          a68g_bufcat (tfilename, chars, BUFFER_SIZE);
 246        }
 247        a68g_bufcat (tfilename, ".tmp", BUFFER_SIZE);
 248        errno = 0;
 249        FILE_T fd = open (tfilename, flags | O_EXCL, permissions);
 250        good_file = (BOOL_T) (fd != A68G_NO_FILE && errno == 0);
 251        if (good_file) {
 252          (void) close (fd);
 253        }
 254      }
 255    }
 256    if (good_file) {
 257      a68g_bufcpy (fn, tfilename, BUFFER_SIZE);
 258      return A68G_TRUE;
 259    } else {
 260      return A68G_FALSE;
 261    }
 262  #undef TMP_SIZE
 263  #undef TRIALS
 264  }
 265  
 266  //! @brief Open a file, or establish it.
 267  
 268  FILE_T open_physical_file (NODE_T * p, A68G_REF ref_file, int flags, mode_t permissions)
 269  {
 270    BOOL_T reading = (flags & ~O_BINARY) == A68G_READ_ACCESS;
 271    BOOL_T writing = (flags & ~O_BINARY) == A68G_WRITE_ACCESS;
 272    ABEND (reading == writing, ERROR_INTERNAL_CONSISTENCY, __func__);
 273    CHECK_REF (p, ref_file, M_REF_FILE);
 274    A68G_FILE *file = FILE_DEREF (&ref_file);
 275    CHECK_INIT (p, INITIALISED (file), M_FILE);
 276    if (!IS_NIL (STRING (file))) {
 277      if (writing) {
 278        A68G_REF z = *DEREF (A68G_REF, &STRING (file));
 279        A68G_ARRAY *arr; A68G_TUPLE *tup;
 280        GET_DESCRIPTOR (arr, tup, &z);
 281        UPB (tup) = LWB (tup) - 1;
 282      }
 283  // Associated file.
 284      TRANSPUT_BUFFER (file) = get_unblocked_transput_buffer (p);
 285      reset_transput_buffer (TRANSPUT_BUFFER (file));
 286      END_OF_FILE (file) = A68G_FALSE;
 287      FILE_ENTRY (file) = -1;
 288      return FD (file);
 289    } else if (IS_NIL (IDENTIFICATION (file))) {
 290  // No identification, so generate a unique identification..
 291      if (reading) {
 292        return A68G_NO_FILE;
 293      } else {
 294        BUFFER tfilename;
 295        BUFCLR (tfilename);
 296        if (!a68g_mkstemp (tfilename, flags, permissions)) {
 297          diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_NO_TEMP);
 298          exit_genie (p, A68G_RUNTIME_ERROR);
 299        }
 300        FD (file) = open (tfilename, flags, permissions);
 301        size_t len = 1 + strlen (tfilename);
 302        IDENTIFICATION (file) = heap_generator (p, M_C_STRING, len);
 303        BLOCK_GC_HANDLE (&(IDENTIFICATION (file)));
 304        a68g_bufcpy (DEREF (char, &IDENTIFICATION (file)), tfilename, len);
 305        TRANSPUT_BUFFER (file) = get_unblocked_transput_buffer (p);
 306        reset_transput_buffer (TRANSPUT_BUFFER (file));
 307        END_OF_FILE (file) = A68G_FALSE;
 308        TMP_FILE (file) = A68G_TRUE;
 309        FILE_ENTRY (file) = store_file_entry (p, FD (file), tfilename, TMP_FILE (file));
 310        return FD (file);
 311      }
 312    } else {
 313  // Opening an identified file.
 314      A68G_REF ref_filename = IDENTIFICATION (file);
 315      CHECK_REF (p, ref_filename, M_ROWS);
 316      char *filename = DEREF (char, &ref_filename);
 317      if (OPEN_EXCLUSIVE (file)) {
 318  // Establishing requires that the file does not exist.
 319        if (flags == (A68G_WRITE_ACCESS)) {
 320          flags |= O_EXCL;
 321        }
 322        OPEN_EXCLUSIVE (file) = A68G_FALSE;
 323      }
 324      FD (file) = open (filename, flags, permissions);
 325      TRANSPUT_BUFFER (file) = get_unblocked_transput_buffer (p);
 326      reset_transput_buffer (TRANSPUT_BUFFER (file));
 327      END_OF_FILE (file) = A68G_FALSE;
 328      FILE_ENTRY (file) = store_file_entry (p, FD (file), filename, TMP_FILE (file));
 329      return FD (file);
 330    }
 331  }
 332  
 333  //! @brief Call PROC (REF FILE) VOID during transput.
 334  
 335  void genie_call_proc_ref_file_void (NODE_T * p, A68G_REF ref_file, A68G_PROCEDURE z)
 336  {
 337    ADDR_T pop_sp = A68G_SP, pop_fp = A68G_FP;
 338    MOID_T *u = M_PROC_REF_FILE_VOID;
 339    PUSH_REF (p, ref_file);
 340    genie_call_procedure (p, MOID (&z), u, u, &z, pop_sp, pop_fp);
 341    A68G_SP = pop_sp;              // Voiding
 342  }
 343  
 344  // Unformatted transput.
 345  
 346  //! @brief Hexadecimal value of digit.
 347  
 348  int char_value (int ch)
 349  {
 350    switch (ch) {
 351    case '0': {
 352        return 0;
 353      }
 354    case '1': {
 355        return 1;
 356      }
 357    case '2': {
 358        return 2;
 359      }
 360    case '3': {
 361        return 3;
 362      }
 363    case '4': {
 364        return 4;
 365      }
 366    case '5': {
 367        return 5;
 368      }
 369    case '6': {
 370        return 6;
 371      }
 372    case '7': {
 373        return 7;
 374      }
 375    case '8': {
 376        return 8;
 377      }
 378    case '9': {
 379        return 9;
 380      }
 381    case 'A':
 382    case 'a': {
 383        return 10;
 384      }
 385    case 'B':
 386    case 'b': {
 387        return 11;
 388      }
 389    case 'C':
 390    case 'c': {
 391        return 12;
 392      }
 393    case 'D':
 394    case 'd': {
 395        return 13;
 396      }
 397    case 'E':
 398    case 'e': {
 399        return 14;
 400      }
 401    case 'F':
 402    case 'f': {
 403        return 15;
 404      }
 405    default: {
 406        return -1;
 407      }
 408    }
 409  }
 410  
 411  //! @brief INT value of BITS denotation
 412  
 413  UNSIGNED_T bits_to_int (NODE_T * p, char *str)
 414  {
 415    errno = 0;
 416    char *radix = NO_TEXT, *end = NO_TEXT;
 417    int base = (int) a68g_strtou (str, &radix, 10);
 418    if (radix != NO_TEXT && TO_UPPER (radix[0]) == TO_UPPER (RADIX_CHAR) && errno == 0) {
 419      UNSIGNED_T bits = 0;
 420      if (base < 2 || base > 16) {
 421        diagnostic (A68G_RUNTIME_ERROR, p, ERROR_INVALID_RADIX, base);
 422        exit_genie (p, A68G_RUNTIME_ERROR);
 423      }
 424      bits = a68g_strtou (&(radix[1]), &end, base);
 425      if (end != NO_TEXT && end[0] == NULL_CHAR && errno == 0) {
 426        return bits;
 427      }
 428    }
 429    diagnostic (A68G_RUNTIME_ERROR, p, ERROR_IN_DENOTATION, M_BITS);
 430    exit_genie (p, A68G_RUNTIME_ERROR);
 431    return 0;
 432  }
 433  
 434  //! @brief Convert string to required mode and store.
 435  
 436  BOOL_T genie_string_to_value_internal (NODE_T * p, MOID_T * m, char *a, BYTE_T * item)
 437  {
 438    errno = 0;
 439  // strto.. does not mind empty strings.
 440    if (strlen (a) == 0) {
 441      return A68G_FALSE;
 442    }
 443    if (m == M_INT) {
 444      A68G_INT *z = (A68G_INT *) item;
 445      char *end;
 446      VALUE (z) = (INT_T) a68g_strtoi (a, &end, 10);
 447      if (end[0] == NULL_CHAR && errno == 0) {
 448        STATUS (z) = INIT_MASK;
 449        return A68G_TRUE;
 450      } else {
 451        return A68G_FALSE;
 452      }
 453    }
 454    if (m == M_REAL) {
 455      A68G_REAL *z = (A68G_REAL *) item;
 456      char *end;
 457      VALUE (z) = strtod (a, &end);
 458      if (end[0] == NULL_CHAR && errno == 0) {
 459        STATUS (z) = INIT_MASK;
 460        return A68G_TRUE;
 461      } else {
 462        return A68G_FALSE;
 463      }
 464    }
 465  #if (A68G_LEVEL >= 3)
 466    if (m == M_LONG_INT) {
 467      A68G_LONG_INT *z = (A68G_LONG_INT *) item;
 468      if (string_to_double_int (p, z, a) == A68G_FALSE) {
 469        return A68G_FALSE;
 470      }
 471      STATUS (z) = INIT_MASK;
 472      return A68G_TRUE;
 473    }
 474    if (m == M_LONG_REAL) {
 475      A68G_LONG_REAL *z = (A68G_LONG_REAL *) item;
 476      char *end;
 477  //  VALUE (z).f = strtoflt128 (a, &end);
 478      VALUE (z).f = string_to_double (a, &end);
 479      MATH_RTE (p, errno != 0, M_LONG_REAL, ERROR_MATH);
 480      if (end[0] == NULL_CHAR && errno == 0) {
 481        STATUS (z) = INIT_MASK;
 482        return A68G_TRUE;
 483      } else {
 484        return A68G_FALSE;
 485      }
 486    }
 487    if (m == M_LONG_BITS) {
 488      A68G_LONG_BITS *z = (A68G_LONG_BITS *) item;
 489      int ret = A68G_TRUE;
 490      DOUBLE_NUM_T b;
 491      set_lw (b, 0x0);
 492      if (a[0] == FLIP_CHAR || a[0] == FLOP_CHAR) {
 493  // [] BOOL denotation is "TTFFFFTFT ...".
 494        if (strlen (a) > (size_t) A68G_LONG_BITS_WIDTH) {
 495          errno = ERANGE;
 496          ret = A68G_FALSE;
 497        } else {
 498          int n = 1;
 499          UNSIGNED_T k = 0x1;
 500          for (INT_T j = (INT_T) strlen (a) - 1; j >= 0; j--) {
 501            if (a[j] == FLIP_CHAR) {
 502              if (n <= A68G_LONG_BITS_WIDTH / 2) {
 503                LW (b) |= k;
 504              } else {
 505                HW (b) |= k;
 506              }
 507            } else if (a[j] != FLOP_CHAR) {
 508              ret = A68G_FALSE;
 509            }
 510            k <<= 1;
 511          }
 512        }
 513        VALUE (z) = b;
 514      } else {
 515  // BITS denotation.
 516        VALUE (z) = double_strtou (p, a);
 517      }
 518      return ret;
 519    }
 520  #else
 521    if (m == M_LONG_BITS || m == M_LONG_LONG_BITS) {
 522      int digits = DIGITS (m);
 523      int status = A68G_TRUE;
 524      ADDR_T pop_sp = A68G_SP;
 525      MP_T *z = (MP_T *) item;
 526      if (a[0] == FLIP_CHAR || a[0] == FLOP_CHAR) {
 527  // [] BOOL denotation is "TTFFFFTFT ...".
 528        if (strlen (a) > (size_t) A68G_BITS_WIDTH) {
 529          errno = ERANGE;
 530          status = A68G_FALSE;
 531        } else {
 532          MP_T *w = lit_mp (p, 1, 0, digits);
 533          SET_MP_ZERO (z, digits);
 534          for (INT_T j = (INT_T) strlen (a) - 1; j >= 0; j--) {
 535            if (a[j] == FLIP_CHAR) {
 536              (void) add_mp (p, z, z, w, digits);
 537            } else if (a[j] != FLOP_CHAR) {
 538              status = A68G_FALSE;
 539            }
 540            (void) mul_mp_digit (p, w, w, (MP_T) 2, digits);
 541          }
 542        }
 543      } else {
 544  // BITS denotation is also allowed.
 545        mp_strtou (p, z, a, m);
 546      }
 547      A68G_SP = pop_sp;
 548      if (errno != 0 || status == A68G_FALSE) {
 549        return A68G_FALSE;
 550      }
 551      MP_STATUS (z) = (MP_T) INIT_MASK;
 552      return A68G_TRUE;
 553    }
 554  #endif
 555    if (m == M_LONG_INT || m == M_LONG_LONG_INT) {
 556      int digits = DIGITS (m);
 557      MP_T *z = (MP_T *) item;
 558      if (strtomp (p, z, a, digits) == NaN_MP) {
 559        return A68G_FALSE;
 560      }
 561      if (!check_mp_int (z, m)) {
 562        errno = ERANGE;
 563        return A68G_FALSE;
 564      }
 565      MP_STATUS (z) = (MP_T) INIT_MASK;
 566      return A68G_TRUE;
 567    }
 568    if (m == M_LONG_REAL || m == M_LONG_LONG_REAL) {
 569      int digits = DIGITS (m);
 570      MP_T *z = (MP_T *) item;
 571      if (strtomp (p, z, a, digits) == NaN_MP) {
 572        return A68G_FALSE;
 573      }
 574      MP_STATUS (z) = (MP_T) INIT_MASK;
 575      return A68G_TRUE;
 576    }
 577    if (m == M_BOOL) {
 578      A68G_BOOL *z = (A68G_BOOL *) item;
 579      char q = a[0], flip = FLIP_CHAR, flop = FLOP_CHAR;
 580      if (q == flip || q == flop) {
 581        VALUE (z) = (BOOL_T) (q == flip);
 582        STATUS (z) = INIT_MASK;
 583        return A68G_TRUE;
 584      } else {
 585        return A68G_FALSE;
 586      }
 587    }
 588    if (m == M_BITS) {
 589      A68G_BITS *z = (A68G_BITS *) item;
 590      int status = A68G_TRUE;
 591      if (a[0] == FLIP_CHAR || a[0] == FLOP_CHAR) {
 592  // [] BOOL denotation is "TTFFFFTFT ...".
 593        if (strlen (a) > (size_t) A68G_BITS_WIDTH) {
 594          errno = ERANGE;
 595          status = A68G_FALSE;
 596        } else {
 597          UNSIGNED_T k = 0x1;
 598          VALUE (z) = 0;
 599          for (INT_T j = (INT_T) strlen (a) - 1; j >= 0; j--) {
 600            if (a[j] == FLIP_CHAR) {
 601              VALUE (z) += k;
 602            } else if (a[j] != FLOP_CHAR) {
 603              status = A68G_FALSE;
 604            }
 605            k <<= 1;
 606          }
 607        }
 608      } else {
 609  // BITS denotation is also allowed.
 610        VALUE (z) = bits_to_int (p, a);
 611      }
 612      if (errno != 0 || status == A68G_FALSE) {
 613        return A68G_FALSE;
 614      }
 615      STATUS (z) = INIT_MASK;
 616      return A68G_TRUE;
 617    }
 618    return A68G_FALSE;
 619  }
 620  
 621  //! @brief Convert string in input buffer to value of required mode.
 622  
 623  void genie_string_to_value (NODE_T * p, MOID_T * mode, BYTE_T * item, A68G_REF ref_file)
 624  {
 625    char *str = get_transput_buffer (INPUT_BUFFER);
 626    errno = 0;
 627  // end string, just in case.
 628    plusab_transput_buffer (p, INPUT_BUFFER, NULL_CHAR);
 629    if (mode == M_INT) {
 630      if (genie_string_to_value_internal (p, mode, str, item) == A68G_FALSE) {
 631        value_error (p, mode, ref_file);
 632      }
 633    } else if (mode == M_LONG_INT || mode == M_LONG_LONG_INT) {
 634      if (genie_string_to_value_internal (p, mode, str, item) == A68G_FALSE) {
 635        value_error (p, mode, ref_file);
 636      }
 637    } else if (mode == M_REAL) {
 638      if (genie_string_to_value_internal (p, mode, str, item) == A68G_FALSE) {
 639        value_error (p, mode, ref_file);
 640      }
 641    } else if (mode == M_LONG_REAL || mode == M_LONG_LONG_REAL) {
 642      if (genie_string_to_value_internal (p, mode, str, item) == A68G_FALSE) {
 643        value_error (p, mode, ref_file);
 644      }
 645    } else if (mode == M_BOOL) {
 646      if (genie_string_to_value_internal (p, mode, str, item) == A68G_FALSE) {
 647        value_error (p, mode, ref_file);
 648      }
 649    } else if (mode == M_BITS) {
 650      if (genie_string_to_value_internal (p, mode, str, item) == A68G_FALSE) {
 651        value_error (p, mode, ref_file);
 652      }
 653    } else if (mode == M_LONG_BITS || mode == M_LONG_LONG_BITS) {
 654      if (genie_string_to_value_internal (p, mode, str, item) == A68G_FALSE) {
 655        value_error (p, mode, ref_file);
 656      }
 657    } else if (mode == M_CHAR) {
 658      A68G_CHAR *z = (A68G_CHAR *) item;
 659      if (str[0] == NULL_CHAR) {
 660  //      value_error (p, mode, ref_file);.
 661        VALUE (z) = NULL_CHAR;
 662        STATUS (z) = INIT_MASK;
 663      } else {
 664        size_t len = strlen (str);
 665        if (len == 0 || len > 1) {
 666          value_error (p, mode, ref_file);
 667        }
 668        VALUE (z) = str[0];
 669        STATUS (z) = INIT_MASK;
 670      }
 671    } else if (mode == M_STRING) {
 672      A68G_REF z;
 673      z = c_to_a_string (p, str, get_transput_buffer_index (INPUT_BUFFER) - 1);
 674      *(A68G_REF *) item = z;
 675    }
 676    if (errno != 0) {
 677      transput_error (p, ref_file, mode);
 678    }
 679  }
 680  
 681  //! @brief Read object from file.
 682  
 683  void genie_read_standard (NODE_T * p, MOID_T * mode, BYTE_T * item, A68G_REF ref_file)
 684  {
 685    A68G_FILE *f = FILE_DEREF (&ref_file);
 686    errno = 0;
 687    if (END_OF_FILE (f)) {
 688      end_of_file_error (p, ref_file);
 689    }
 690    if (mode == M_PROC_REF_FILE_VOID) {
 691      genie_call_proc_ref_file_void (p, ref_file, *(A68G_PROCEDURE *) item);
 692    } else if (mode == M_FORMAT) {
 693      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_UNDEFINED_TRANSPUT, M_FORMAT);
 694      exit_genie (p, A68G_RUNTIME_ERROR);
 695    } else if (mode == M_REF_SOUND) {
 696      read_sound (p, ref_file, DEREF (A68G_SOUND, (A68G_REF *) item));
 697    } else if (IS_REF (mode)) {
 698      CHECK_REF (p, *(A68G_REF *) item, mode);
 699      genie_read_standard (p, SUB (mode), ADDRESS ((A68G_REF *) item), ref_file);
 700    } else if (mode == M_INT || mode == M_LONG_INT || mode == M_LONG_LONG_INT) {
 701      scan_integer (p, ref_file);
 702      genie_string_to_value (p, mode, item, ref_file);
 703    } else if (mode == M_REAL || mode == M_LONG_REAL || mode == M_LONG_LONG_REAL) {
 704      scan_real (p, ref_file);
 705      genie_string_to_value (p, mode, item, ref_file);
 706    } else if (mode == M_BOOL) {
 707      scan_char (p, ref_file);
 708      genie_string_to_value (p, mode, item, ref_file);
 709    } else if (mode == M_CHAR) {
 710      scan_char (p, ref_file);
 711      genie_string_to_value (p, mode, item, ref_file);
 712    } else if (mode == M_BITS || mode == M_LONG_BITS || mode == M_LONG_LONG_BITS) {
 713      scan_bits (p, ref_file);
 714      genie_string_to_value (p, mode, item, ref_file);
 715    } else if (mode == M_STRING) {
 716      char *term = DEREF (char, &TERMINATOR (f));
 717      scan_string (p, term, ref_file);
 718      genie_string_to_value (p, mode, item, ref_file);
 719    } else if (IS_STRUCT (mode)) {
 720      for (PACK_T *q = PACK (mode); q != NO_PACK; FORWARD (q)) {
 721        genie_read_standard (p, MOID (q), &item[OFFSET (q)], ref_file);
 722      }
 723    } else if (IS_UNION (mode)) {
 724      A68G_UNION *z = (A68G_UNION *) item;
 725      if (!(STATUS (z) | INIT_MASK) || VALUE (z) == NULL) {
 726        diagnostic (A68G_RUNTIME_ERROR, p, ERROR_EMPTY_VALUE, mode);
 727        exit_genie (p, A68G_RUNTIME_ERROR);
 728      }
 729      genie_read_standard (p, (MOID_T *) (VALUE (z)), &item[A68G_UNION_SIZE], ref_file);
 730    } else if (IS_ROW (mode) || IS_FLEX (mode)) {
 731      MOID_T *deflexed = DEFLEX (mode);
 732      A68G_ARRAY *arr;
 733      A68G_TUPLE *tup;
 734      CHECK_INIT (p, INITIALISED ((A68G_REF *) item), mode);
 735      GET_DESCRIPTOR (arr, tup, (A68G_REF *) item);
 736      if (get_row_size (tup, DIM (arr)) > 0) {
 737        BYTE_T *base_addr = DEREF (BYTE_T, &ARRAY (arr));
 738        BOOL_T done = A68G_FALSE;
 739        initialise_internal_index (tup, DIM (arr));
 740        while (!done) {
 741          ADDR_T a68g_index = calculate_internal_index (tup, DIM (arr));
 742          ADDR_T elem_addr = ROW_ELEMENT (arr, a68g_index);
 743          genie_read_standard (p, SUB (deflexed), &base_addr[elem_addr], ref_file);
 744          done = increment_internal_index (tup, DIM (arr));
 745        }
 746      }
 747    }
 748    if (errno != 0) {
 749      transput_error (p, ref_file, mode);
 750    }
 751  }
 752  
 753  //! @brief PROC ([] SIMPLIN) VOID read
 754  
 755  void genie_read (NODE_T * p)
 756  {
 757    A68G_REF row;
 758    POP_REF (p, &row);
 759    genie_stand_in (p);
 760    PUSH_REF (p, row);
 761    genie_read_file (p);
 762  }
 763  
 764  //! @brief Open for reading.
 765  
 766  void open_for_reading (NODE_T * p, A68G_REF ref_file)
 767  {
 768    A68G_FILE *file = FILE_DEREF (&ref_file);
 769    if (!OPENED (file)) {
 770      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
 771      exit_genie (p, A68G_RUNTIME_ERROR);
 772    }
 773    if (DRAW_MOOD (file)) {
 774      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
 775      exit_genie (p, A68G_RUNTIME_ERROR);
 776    }
 777    if (WRITE_MOOD (file)) {
 778      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "write");
 779      exit_genie (p, A68G_RUNTIME_ERROR);
 780    }
 781    if (!GET (&CHANNEL (file))) {
 782      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "getting");
 783      exit_genie (p, A68G_RUNTIME_ERROR);
 784    }
 785    if (!READ_MOOD (file) && !WRITE_MOOD (file)) {
 786      if (IS_NIL (STRING (file))) {
 787        if ((FD (file) = open_physical_file (p, ref_file, A68G_READ_ACCESS, 0)) == A68G_NO_FILE) {
 788          open_error (p, ref_file, "getting");
 789        }
 790      } else {
 791        FD (file) = open_physical_file (p, ref_file, A68G_READ_ACCESS, 0);
 792      }
 793      DRAW_MOOD (file) = A68G_FALSE;
 794      READ_MOOD (file) = A68G_TRUE;
 795      WRITE_MOOD (file) = A68G_FALSE;
 796      CHAR_MOOD (file) = A68G_TRUE;
 797    }
 798    if (!CHAR_MOOD (file)) {
 799      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "binary");
 800      exit_genie (p, A68G_RUNTIME_ERROR);
 801    }
 802  }
 803  
 804  //! @brief PROC (REF FILE, [] SIMPLIN) VOID get
 805  
 806  void genie_read_file (NODE_T * p)
 807  {
 808    A68G_REF row; A68G_ARRAY *arr; A68G_TUPLE *tup;
 809    POP_REF (p, &row);
 810    CHECK_REF (p, row, M_ROW_SIMPLIN);
 811    GET_DESCRIPTOR (arr, tup, &row);
 812    int elems = ROW_SIZE (tup);
 813    A68G_REF ref_file;
 814    POP_REF (p, &ref_file);
 815    CHECK_REF (p, ref_file, M_REF_FILE);
 816    A68G_FILE *file = FILE_DEREF (&ref_file);
 817    CHECK_INIT (p, INITIALISED (file), M_FILE);
 818    open_for_reading (p, ref_file);
 819  // Read.
 820    if (elems <= 0) {
 821      return;
 822    }
 823    BYTE_T *base_address = DEREF (BYTE_T, &ARRAY (arr));
 824    int elem_index = 0;
 825    for (int k = 0; k < elems; k++) {
 826      A68G_UNION *z = (A68G_UNION *) & base_address[elem_index];
 827      MOID_T *mode = (MOID_T *) (VALUE (z));
 828      BYTE_T *item = (BYTE_T *) & base_address[elem_index + A68G_UNION_SIZE];
 829      genie_read_standard (p, mode, item, ref_file);
 830      elem_index += SIZE (M_SIMPLIN);
 831    }
 832  }
 833  
 834  //! @brief Convert value to string.
 835  
 836  void genie_value_to_string (NODE_T * p, MOID_T * moid, BYTE_T * item, int mod)
 837  {
 838    if (moid == M_INT) {
 839      A68G_INT *z = (A68G_INT *) item;
 840      PUSH_UNION (p, M_INT);
 841      PUSH_VALUE (p, VALUE (z), A68G_INT);
 842      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68G_UNION_SIZE + SIZE (M_INT)));
 843      if (mod == FORMAT_ITEM_G) {
 844        PUSH_VALUE (p, A68G_INT_WIDTH + 1, A68G_INT);
 845        genie_whole (p);
 846      } else if (mod == FORMAT_ITEM_H) {
 847        PUSH_VALUE (p, A68G_REAL_WIDTH + A68G_EXP_WIDTH + 4, A68G_INT);
 848        PUSH_VALUE (p, A68G_REAL_WIDTH - 1, A68G_INT);
 849        PUSH_VALUE (p, A68G_EXP_WIDTH + 1, A68G_INT);
 850        PUSH_VALUE (p, 3, A68G_INT);
 851        genie_real (p);
 852      }
 853      return;
 854    }
 855  #if (A68G_LEVEL >= 3)
 856    if (moid == M_LONG_INT) {
 857      A68G_LONG_INT *z = (A68G_LONG_INT *) item;
 858      PUSH_UNION (p, M_LONG_INT);
 859      PUSH (p, z, SIZE (M_LONG_INT));
 860      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68G_UNION_SIZE + SIZE (M_LONG_INT)));
 861      if (mod == FORMAT_ITEM_G) {
 862        PUSH_VALUE (p, A68G_LONG_WIDTH + 1, A68G_INT);
 863        genie_whole (p);
 864      } else if (mod == FORMAT_ITEM_H) {
 865        PUSH_VALUE (p, A68G_LONG_REAL_WIDTH + A68G_LONG_EXP_WIDTH + 4, A68G_INT);
 866        PUSH_VALUE (p, A68G_LONG_REAL_WIDTH - 1, A68G_INT);
 867        PUSH_VALUE (p, A68G_LONG_EXP_WIDTH + 1, A68G_INT);
 868        PUSH_VALUE (p, 3, A68G_INT);
 869        genie_real (p);
 870      }
 871      return;
 872    }
 873    if (moid == M_LONG_REAL) {
 874      A68G_LONG_REAL *z = (A68G_LONG_REAL *) item;
 875      PUSH_UNION (p, M_LONG_REAL);
 876      PUSH_VALUE (p, VALUE (z), A68G_LONG_REAL);
 877      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68G_UNION_SIZE + SIZE (M_LONG_REAL)));
 878      PUSH_VALUE (p, A68G_LONG_REAL_WIDTH + A68G_LONG_EXP_WIDTH + 4, A68G_INT);
 879      PUSH_VALUE (p, A68G_LONG_REAL_WIDTH - 1, A68G_INT);
 880      PUSH_VALUE (p, A68G_LONG_EXP_WIDTH + 1, A68G_INT);
 881      if (mod == FORMAT_ITEM_G) {
 882        genie_float (p);
 883      } else if (mod == FORMAT_ITEM_H) {
 884        PUSH_VALUE (p, 3, A68G_INT);
 885        genie_real (p);
 886      }
 887      return;
 888    }
 889    if (moid == M_LONG_BITS) {
 890      A68G_LONG_BITS *z = (A68G_LONG_BITS *) item;
 891      char *s = stack_string (p, 8 + A68G_LONG_BITS_WIDTH);
 892      int n = 0;
 893      for (int w = 0; w <= 1; w++) {
 894        UNSIGNED_T bit = D_SIGN;
 895        for (int j = 0; j < A68G_BITS_WIDTH; j++) {
 896          if (w == 0) {
 897            s[n] = (char) ((HW (VALUE (z)) & bit) ? FLIP_CHAR : FLOP_CHAR);
 898          } else {
 899            s[n] = (char) ((LW (VALUE (z)) & bit) ? FLIP_CHAR : FLOP_CHAR);
 900          }
 901          bit >>= 1;
 902          n++;
 903        }
 904      }
 905      s[n] = NULL_CHAR;
 906      return;
 907    }
 908  #else
 909    if (moid == M_LONG_BITS || moid == M_LONG_LONG_BITS) {
 910      int bits = get_mp_bits_width (moid), word = get_mp_bits_words (moid);
 911      int pos = bits;
 912      char *str = stack_string (p, 8 + bits);
 913      ADDR_T pop_sp = A68G_SP;
 914      unt *row = stack_mp_bits (p, (MP_T *) item, moid);
 915      str[pos--] = NULL_CHAR;
 916      while (pos >= 0) {
 917        unt bit = 0x1;
 918        for (int j = 0; j < MP_BITS_BITS && pos >= 0; j++) {
 919          str[pos--] = (char) ((row[word - 1] & bit) ? FLIP_CHAR : FLOP_CHAR);
 920          bit <<= 1;
 921        }
 922        word--;
 923      }
 924      A68G_SP = pop_sp;
 925      return;
 926    }
 927  #endif
 928    if (moid == M_LONG_INT) {
 929      MP_T *z = (MP_T *) item;
 930      PUSH_UNION (p, M_LONG_INT);
 931      PUSH (p, z, SIZE (M_LONG_INT));
 932      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68G_UNION_SIZE + SIZE (M_LONG_INT)));
 933      if (mod == FORMAT_ITEM_G) {
 934        PUSH_VALUE (p, A68G_LONG_WIDTH + 1, A68G_INT);
 935        genie_whole (p);
 936      } else if (mod == FORMAT_ITEM_H) {
 937        PUSH_VALUE (p, A68G_LONG_REAL_WIDTH + A68G_LONG_EXP_WIDTH + 4, A68G_INT);
 938        PUSH_VALUE (p, A68G_LONG_REAL_WIDTH - 1, A68G_INT);
 939        PUSH_VALUE (p, A68G_LONG_EXP_WIDTH + 1, A68G_INT);
 940        PUSH_VALUE (p, 3, A68G_INT);
 941        genie_real (p);
 942      }
 943      return;
 944    }
 945    if (moid == M_LONG_LONG_INT) {
 946      MP_T *z = (MP_T *) item;
 947      PUSH_UNION (p, M_LONG_LONG_INT);
 948      PUSH (p, z, SIZE (M_LONG_LONG_INT));
 949      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68G_UNION_SIZE + SIZE (M_LONG_LONG_INT)));
 950      if (mod == FORMAT_ITEM_G) {
 951        PUSH_VALUE (p, A68G_LONG_LONG_WIDTH + 1, A68G_INT);
 952        genie_whole (p);
 953      } else if (mod == FORMAT_ITEM_H) {
 954        PUSH_VALUE (p, A68G_LONG_LONG_REAL_WIDTH + A68G_LONG_LONG_EXP_WIDTH + 4, A68G_INT);
 955        PUSH_VALUE (p, A68G_LONG_LONG_REAL_WIDTH - 1, A68G_INT);
 956        PUSH_VALUE (p, A68G_LONG_LONG_EXP_WIDTH + 1, A68G_INT);
 957        PUSH_VALUE (p, 3, A68G_INT);
 958        genie_real (p);
 959      }
 960      return;
 961    }
 962    if (moid == M_REAL) {
 963      A68G_REAL *z = (A68G_REAL *) item;
 964      PUSH_UNION (p, M_REAL);
 965      PUSH_VALUE (p, VALUE (z), A68G_REAL);
 966      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68G_UNION_SIZE + SIZE (M_REAL)));
 967      PUSH_VALUE (p, A68G_REAL_WIDTH + A68G_EXP_WIDTH + 4, A68G_INT);
 968      PUSH_VALUE (p, A68G_REAL_WIDTH - 1, A68G_INT);
 969      PUSH_VALUE (p, A68G_EXP_WIDTH + 1, A68G_INT);
 970      if (mod == FORMAT_ITEM_G) {
 971        genie_float (p);
 972      } else if (mod == FORMAT_ITEM_H) {
 973        PUSH_VALUE (p, 3, A68G_INT);
 974        genie_real (p);
 975      }
 976      return;
 977    }
 978    if (moid == M_LONG_REAL) {
 979      MP_T *z = (MP_T *) item;
 980      PUSH_UNION (p, M_LONG_REAL);
 981      PUSH (p, z, (int) SIZE (M_LONG_REAL));
 982      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68G_UNION_SIZE + SIZE (M_LONG_REAL)));
 983      PUSH_VALUE (p, A68G_LONG_REAL_WIDTH + A68G_LONG_EXP_WIDTH + 4, A68G_INT);
 984      PUSH_VALUE (p, A68G_LONG_REAL_WIDTH - 1, A68G_INT);
 985      PUSH_VALUE (p, A68G_LONG_EXP_WIDTH + 1, A68G_INT);
 986      if (mod == FORMAT_ITEM_G) {
 987        genie_float (p);
 988      } else if (mod == FORMAT_ITEM_H) {
 989        PUSH_VALUE (p, 3, A68G_INT);
 990        genie_real (p);
 991      }
 992      return;
 993    }
 994    if (moid == M_LONG_LONG_REAL) {
 995      MP_T *z = (MP_T *) item;
 996      PUSH_UNION (p, M_LONG_LONG_REAL);
 997      PUSH (p, z, (int) SIZE (M_LONG_LONG_REAL));
 998      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68G_UNION_SIZE + SIZE (M_LONG_LONG_REAL)));
 999      PUSH_VALUE (p, A68G_LONG_LONG_REAL_WIDTH + A68G_LONG_LONG_EXP_WIDTH + 4, A68G_INT);
1000      PUSH_VALUE (p, A68G_LONG_LONG_REAL_WIDTH - 1, A68G_INT);
1001      PUSH_VALUE (p, A68G_LONG_LONG_EXP_WIDTH + 1, A68G_INT);
1002      if (mod == FORMAT_ITEM_G) {
1003        genie_float (p);
1004      } else if (mod == FORMAT_ITEM_H) {
1005        PUSH_VALUE (p, 3, A68G_INT);
1006        genie_real (p);
1007      }
1008      return;
1009    }
1010    if (moid == M_BITS) {
1011      A68G_BITS *z = (A68G_BITS *) item;
1012      char *str = stack_string (p, 8 + A68G_BITS_WIDTH);
1013      UNSIGNED_T bit = 0x1;
1014      int j;
1015      for (j = 1; j < A68G_BITS_WIDTH; j++) {
1016        bit <<= 1;
1017      }
1018      for (j = 0; j < A68G_BITS_WIDTH; j++) {
1019        str[j] = (char) ((VALUE (z) & bit) ? FLIP_CHAR : FLOP_CHAR);
1020        bit >>= 1;
1021      }
1022      str[j] = NULL_CHAR;
1023      return;
1024    }
1025  }
1026  
1027  //! @brief Print object to file.
1028  
1029  void genie_write_standard (NODE_T * p, MOID_T * mode, BYTE_T * item, A68G_REF ref_file)
1030  {
1031    errno = 0;
1032    ABEND (mode == NO_MOID, ERROR_INTERNAL_CONSISTENCY, __func__);
1033    if (mode == M_PROC_REF_FILE_VOID) {
1034      genie_call_proc_ref_file_void (p, ref_file, *(A68G_PROCEDURE *) item);
1035    } else if (mode == M_FORMAT) {
1036      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_UNDEFINED_TRANSPUT, M_FORMAT);
1037      exit_genie (p, A68G_RUNTIME_ERROR);
1038    } else if (mode == M_SOUND) {
1039      write_sound (p, ref_file, (A68G_SOUND *) item);
1040    } else if (mode == M_INT || mode == M_LONG_INT || mode == M_LONG_LONG_INT) {
1041      genie_value_to_string (p, mode, item, FORMAT_ITEM_G);
1042      add_string_from_stack_transput_buffer (p, UNFORMATTED_BUFFER);
1043    } else if (mode == M_REAL || mode == M_LONG_REAL || mode == M_LONG_LONG_REAL) {
1044      genie_value_to_string (p, mode, item, FORMAT_ITEM_G);
1045      add_string_from_stack_transput_buffer (p, UNFORMATTED_BUFFER);
1046    } else if (mode == M_BOOL) {
1047      A68G_BOOL *z = (A68G_BOOL *) item;
1048      char flipflop = (char) (VALUE (z) == A68G_TRUE ? FLIP_CHAR : FLOP_CHAR);
1049      plusab_transput_buffer (p, UNFORMATTED_BUFFER, flipflop);
1050    } else if (mode == M_CHAR) {
1051      A68G_CHAR *ch = (A68G_CHAR *) item;
1052      plusab_transput_buffer (p, UNFORMATTED_BUFFER, (char) VALUE (ch));
1053    } else if (mode == M_BITS || mode == M_LONG_BITS || mode == M_LONG_LONG_BITS) {
1054      char *str = (char *) STACK_TOP;
1055      genie_value_to_string (p, mode, item, FORMAT_ITEM_G);
1056      add_string_transput_buffer (p, UNFORMATTED_BUFFER, str);
1057    } else if (mode == M_ROW_CHAR || mode == M_STRING) {
1058  // Handle these separately since this is faster than straightening.
1059      add_a_string_transput_buffer (p, UNFORMATTED_BUFFER, item);
1060    } else if (IS_UNION (mode)) {
1061      A68G_UNION *z = (A68G_UNION *) item;
1062      MOID_T *um = (MOID_T *) (VALUE (z));
1063      BYTE_T *ui = &item[A68G_UNION_SIZE];
1064      if (um == NO_MOID) {
1065        diagnostic (A68G_RUNTIME_ERROR, p, ERROR_EMPTY_VALUE, mode);
1066        exit_genie (p, A68G_RUNTIME_ERROR);
1067      }
1068      genie_write_standard (p, um, ui, ref_file);
1069    } else if (IS_STRUCT (mode)) {
1070      for (PACK_T *q = PACK (mode); q != NO_PACK; FORWARD (q)) {
1071        BYTE_T *elem = &item[OFFSET (q)];
1072        genie_check_initialisation (p, elem, MOID (q));
1073        genie_write_standard (p, MOID (q), elem, ref_file);
1074      }
1075    } else if (IS_ROW (mode) || IS_FLEX (mode)) {
1076      MOID_T *deflexed = DEFLEX (mode);
1077      A68G_ARRAY *arr;
1078      A68G_TUPLE *tup;
1079      CHECK_INIT (p, INITIALISED ((A68G_REF *) item), M_ROWS);
1080      GET_DESCRIPTOR (arr, tup, (A68G_REF *) item);
1081      if (get_row_size (tup, DIM (arr)) > 0) {
1082        BYTE_T *base_addr = DEREF (BYTE_T, &ARRAY (arr));
1083        BOOL_T done = A68G_FALSE;
1084        initialise_internal_index (tup, DIM (arr));
1085        while (!done) {
1086          ADDR_T a68g_index = calculate_internal_index (tup, DIM (arr));
1087          ADDR_T elem_addr = ROW_ELEMENT (arr, a68g_index);
1088          BYTE_T *elem = &base_addr[elem_addr];
1089          genie_check_initialisation (p, elem, SUB (deflexed));
1090          genie_write_standard (p, SUB (deflexed), elem, ref_file);
1091          done = increment_internal_index (tup, DIM (arr));
1092        }
1093      }
1094    }
1095    if (errno != 0) {
1096      ABEND (IS_NIL (ref_file), ERROR_ACTION, error_specification ());
1097      transput_error (p, ref_file, mode);
1098    }
1099  }
1100  
1101  //! @brief PROC ([] SIMPLOUT) VOID print, write
1102  
1103  void genie_write (NODE_T * p)
1104  {
1105    A68G_REF row;
1106    POP_REF (p, &row);
1107    genie_stand_out (p);
1108    PUSH_REF (p, row);
1109    genie_write_file (p);
1110  }
1111  
1112  //! @brief Open for writing.
1113  
1114  void open_for_writing (NODE_T * p, A68G_REF ref_file)
1115  {
1116    A68G_FILE *file = FILE_DEREF (&ref_file);
1117    if (!OPENED (file)) {
1118      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
1119      exit_genie (p, A68G_RUNTIME_ERROR);
1120    }
1121    if (DRAW_MOOD (file)) {
1122      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
1123      exit_genie (p, A68G_RUNTIME_ERROR);
1124    }
1125    if (READ_MOOD (file)) {
1126      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "read");
1127      exit_genie (p, A68G_RUNTIME_ERROR);
1128    }
1129    if (!PUT (&CHANNEL (file))) {
1130      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "putting");
1131      exit_genie (p, A68G_RUNTIME_ERROR);
1132    }
1133    if (!READ_MOOD (file) && !WRITE_MOOD (file)) {
1134      if (IS_NIL (STRING (file))) {
1135        if ((FD (file) = open_physical_file (p, ref_file, A68G_WRITE_ACCESS, A68G_PROTECTION)) == A68G_NO_FILE) {
1136          open_error (p, ref_file, "putting");
1137        }
1138      } else {
1139        FD (file) = open_physical_file (p, ref_file, A68G_WRITE_ACCESS, 0);
1140      }
1141      DRAW_MOOD (file) = A68G_FALSE;
1142      READ_MOOD (file) = A68G_FALSE;
1143      WRITE_MOOD (file) = A68G_TRUE;
1144      CHAR_MOOD (file) = A68G_TRUE;
1145    }
1146    if (!CHAR_MOOD (file)) {
1147      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "binary");
1148      exit_genie (p, A68G_RUNTIME_ERROR);
1149    }
1150  }
1151  
1152  //! @brief PROC (REF FILE, [] SIMPLOUT) VOID put
1153  
1154  void genie_write_file (NODE_T * p)
1155  {
1156    A68G_REF row; A68G_ARRAY *arr; A68G_TUPLE *tup;
1157    POP_REF (p, &row);
1158    CHECK_REF (p, row, M_ROW_SIMPLOUT);
1159    GET_DESCRIPTOR (arr, tup, &row);
1160    int elems = ROW_SIZE (tup);
1161    A68G_REF ref_file;
1162    POP_REF (p, &ref_file);
1163    CHECK_REF (p, ref_file, M_REF_FILE);
1164    A68G_FILE *file = FILE_DEREF (&ref_file);
1165    CHECK_INIT (p, INITIALISED (file), M_FILE);
1166    open_for_writing (p, ref_file);
1167  // Write.
1168    if (elems <= 0) {
1169      return;
1170    }
1171    BYTE_T *base_address = DEREF (BYTE_T, &ARRAY (arr));
1172    int elem_index = 0;
1173    for (int k = 0; k < elems; k++) {
1174      A68G_UNION *z = (A68G_UNION *) & (base_address[elem_index]);
1175      MOID_T *mode = (MOID_T *) (VALUE (z));
1176      BYTE_T *item = (BYTE_T *) & base_address[elem_index + A68G_UNION_SIZE];
1177      reset_transput_buffer (UNFORMATTED_BUFFER);
1178      genie_write_standard (p, mode, item, ref_file);
1179      write_purge_buffer (p, ref_file, UNFORMATTED_BUFFER);
1180      elem_index += SIZE (M_SIMPLOUT);
1181    }
1182  }
1183  
1184  //! @brief Read object binary from file.
1185  
1186  void genie_read_bin_standard (NODE_T * p, MOID_T * mode, BYTE_T * item, A68G_REF ref_file)
1187  {
1188    CHECK_REF (p, ref_file, M_REF_FILE);
1189    A68G_FILE *f = FILE_DEREF (&ref_file);
1190    errno = 0;
1191    if (END_OF_FILE (f)) {
1192      end_of_file_error (p, ref_file);
1193    }
1194    if (mode == M_PROC_REF_FILE_VOID) {
1195      genie_call_proc_ref_file_void (p, ref_file, *(A68G_PROCEDURE *) item);
1196    } else if (mode == M_FORMAT) {
1197      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_UNDEFINED_TRANSPUT, M_FORMAT);
1198      exit_genie (p, A68G_RUNTIME_ERROR);
1199    } else if (mode == M_REF_SOUND) {
1200      read_sound (p, ref_file, (A68G_SOUND *) ADDRESS ((A68G_REF *) item));
1201    } else if (IS_REF (mode)) {
1202      CHECK_REF (p, *(A68G_REF *) item, mode);
1203      genie_read_bin_standard (p, SUB (mode), ADDRESS ((A68G_REF *) item), ref_file);
1204    } else if (mode == M_INT) {
1205      A68G_INT *z = (A68G_INT *) item;
1206      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1207      STATUS (z) = INIT_MASK;
1208    } else if (mode == M_LONG_INT) {
1209  #if (A68G_LEVEL >= 3)
1210      A68G_LONG_INT *z = (A68G_LONG_INT *) item;
1211      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1212      STATUS (z) = INIT_MASK;
1213  #else
1214      MP_T *z = (MP_T *) item;
1215      ASSERT (io_read (FD (f), z, (size_t) SIZE (mode)) != -1);
1216      MP_STATUS (z) = (MP_T) INIT_MASK;
1217  #endif
1218    } else if (mode == M_LONG_LONG_INT) {
1219      MP_T *z = (MP_T *) item;
1220      ASSERT (io_read (FD (f), z, (size_t) SIZE (mode)) != -1);
1221      MP_STATUS (z) = (MP_T) INIT_MASK;
1222    } else if (mode == M_REAL) {
1223      A68G_REAL *z = (A68G_REAL *) item;
1224      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1225      STATUS (z) = INIT_MASK;
1226    } else if (mode == M_LONG_REAL) {
1227  #if (A68G_LEVEL >= 3)
1228      A68G_LONG_REAL *z = (A68G_LONG_REAL *) item;
1229      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1230      STATUS (z) = INIT_MASK;
1231  #else
1232      MP_T *z = (MP_T *) item;
1233      ASSERT (io_read (FD (f), z, (size_t) SIZE (mode)) != -1);
1234      MP_STATUS (z) = (MP_T) INIT_MASK;
1235  #endif
1236    } else if (mode == M_LONG_LONG_REAL) {
1237      MP_T *z = (MP_T *) item;
1238      ASSERT (io_read (FD (f), z, (size_t) SIZE (mode)) != -1);
1239      MP_STATUS (z) = (MP_T) INIT_MASK;
1240    } else if (mode == M_BOOL) {
1241      A68G_BOOL *z = (A68G_BOOL *) item;
1242      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1243      STATUS (z) = INIT_MASK;
1244    } else if (mode == M_CHAR) {
1245      A68G_CHAR *z = (A68G_CHAR *) item;
1246      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1247      STATUS (z) = INIT_MASK;
1248    } else if (mode == M_BITS) {
1249      A68G_BITS *z = (A68G_BITS *) item;
1250      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1251      STATUS (z) = INIT_MASK;
1252    } else if (mode == M_LONG_BITS) {
1253  #if (A68G_LEVEL >= 3)
1254      A68G_LONG_BITS *z = (A68G_LONG_BITS *) item;
1255      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1256      STATUS (z) = INIT_MASK;
1257  #else
1258      MP_T *z = (MP_T *) item;
1259      ASSERT (io_read (FD (f), z, (size_t) SIZE (mode)) != -1);
1260      MP_STATUS (z) = (MP_T) INIT_MASK;
1261  #endif
1262    } else if (mode == M_LONG_LONG_BITS) {
1263      MP_T *z = (MP_T *) item;
1264      ASSERT (io_read (FD (f), z, (size_t) SIZE (mode)) != -1);
1265      MP_STATUS (z) = (MP_T) INIT_MASK;
1266    } else if (mode == M_ROW_CHAR || mode == M_STRING) {
1267      int len;
1268      ASSERT (io_read (FD (f), &(len), sizeof (len)) != -1);
1269      reset_transput_buffer (UNFORMATTED_BUFFER);
1270      for (int k = 0; k < len; k++) {
1271        char ch;
1272        ASSERT (io_read (FD (f), &(ch), sizeof (char)) != -1);
1273        plusab_transput_buffer (p, UNFORMATTED_BUFFER, ch);
1274      }
1275      *(A68G_REF *) item = c_to_a_string (p, get_transput_buffer (UNFORMATTED_BUFFER), DEFAULT_WIDTH);
1276    } else if (IS_UNION (mode)) {
1277      A68G_UNION *z = (A68G_UNION *) item;
1278      if (!(STATUS (z) | INIT_MASK) || VALUE (z) == NULL) {
1279        diagnostic (A68G_RUNTIME_ERROR, p, ERROR_EMPTY_VALUE, mode);
1280        exit_genie (p, A68G_RUNTIME_ERROR);
1281      }
1282      genie_read_bin_standard (p, (MOID_T *) (VALUE (z)), &item[A68G_UNION_SIZE], ref_file);
1283    } else if (IS_STRUCT (mode)) {
1284      for (PACK_T *q = PACK (mode); q != NO_PACK; FORWARD (q)) {
1285        genie_read_bin_standard (p, MOID (q), &item[OFFSET (q)], ref_file);
1286      }
1287    } else if (IS_ROW (mode) || IS_FLEX (mode)) {
1288      MOID_T *deflexed = DEFLEX (mode);
1289      A68G_ARRAY *arr; A68G_TUPLE *tup;
1290      CHECK_INIT (p, INITIALISED ((A68G_REF *) item), M_ROWS);
1291      GET_DESCRIPTOR (arr, tup, (A68G_REF *) item);
1292      if (get_row_size (tup, DIM (arr)) > 0) {
1293        BYTE_T *base_addr = DEREF (BYTE_T, &ARRAY (arr));
1294        BOOL_T done = A68G_FALSE;
1295        initialise_internal_index (tup, DIM (arr));
1296        while (!done) {
1297          ADDR_T a68g_index = calculate_internal_index (tup, DIM (arr));
1298          ADDR_T elem_addr = ROW_ELEMENT (arr, a68g_index);
1299          genie_read_bin_standard (p, SUB (deflexed), &base_addr[elem_addr], ref_file);
1300          done = increment_internal_index (tup, DIM (arr));
1301        }
1302      }
1303    }
1304    if (errno != 0) {
1305      transput_error (p, ref_file, mode);
1306    }
1307  }
1308  
1309  //! @brief PROC ([] SIMPLIN) VOID read bin
1310  
1311  void genie_read_bin (NODE_T * p)
1312  {
1313    A68G_REF row;
1314    POP_REF (p, &row);
1315    genie_stand_back (p);
1316    PUSH_REF (p, row);
1317    genie_read_bin_file (p);
1318  }
1319  
1320  //! @brief PROC (REF FILE, [] SIMPLIN) VOID get bin
1321  
1322  void genie_read_bin_file (NODE_T * p)
1323  {
1324    A68G_REF row; A68G_ARRAY *arr; A68G_TUPLE *tup;
1325    POP_REF (p, &row);
1326    CHECK_REF (p, row, M_ROW_SIMPLIN);
1327    GET_DESCRIPTOR (arr, tup, &row);
1328    int elems = ROW_SIZE (tup);
1329    A68G_REF ref_file;
1330    POP_REF (p, &ref_file);
1331    ref_file = *(A68G_REF *) STACK_TOP;
1332    CHECK_REF (p, ref_file, M_REF_FILE);
1333    A68G_FILE *file = FILE_DEREF (&ref_file);
1334    CHECK_INIT (p, INITIALISED (file), M_FILE);
1335    if (!OPENED (file)) {
1336      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
1337      exit_genie (p, A68G_RUNTIME_ERROR);
1338    }
1339    if (DRAW_MOOD (file)) {
1340      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
1341      exit_genie (p, A68G_RUNTIME_ERROR);
1342    }
1343    if (WRITE_MOOD (file)) {
1344      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "write");
1345      exit_genie (p, A68G_RUNTIME_ERROR);
1346    }
1347    if (!GET (&CHANNEL (file))) {
1348      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "getting");
1349      exit_genie (p, A68G_RUNTIME_ERROR);
1350    }
1351    if (!BIN (&CHANNEL (file))) {
1352      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "binary getting");
1353      exit_genie (p, A68G_RUNTIME_ERROR);
1354    }
1355    if (!READ_MOOD (file) && !WRITE_MOOD (file)) {
1356      if ((FD (file) = open_physical_file (p, ref_file, A68G_READ_ACCESS | O_BINARY, 0)) == A68G_NO_FILE) {
1357        open_error (p, ref_file, "binary getting");
1358      }
1359      DRAW_MOOD (file) = A68G_FALSE;
1360      READ_MOOD (file) = A68G_TRUE;
1361      WRITE_MOOD (file) = A68G_FALSE;
1362      CHAR_MOOD (file) = A68G_FALSE;
1363    }
1364    if (CHAR_MOOD (file)) {
1365      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "text");
1366      exit_genie (p, A68G_RUNTIME_ERROR);
1367    }
1368  // Read.
1369    if (elems <= 0) {
1370      return;
1371    }
1372    int elem_index = 0;
1373    BYTE_T *base_address = DEREF (BYTE_T, &ARRAY (arr));
1374    for (int k = 0; k < elems; k++) {
1375      A68G_UNION *z = (A68G_UNION *) & base_address[elem_index];
1376      MOID_T *mode = (MOID_T *) (VALUE (z));
1377      BYTE_T *item = (BYTE_T *) & base_address[elem_index + A68G_UNION_SIZE];
1378      genie_read_bin_standard (p, mode, item, ref_file);
1379      elem_index += SIZE (M_SIMPLIN);
1380    }
1381  }
1382  
1383  //! @brief Write object binary to file.
1384  
1385  void genie_write_bin_standard (NODE_T * p, MOID_T * mode, BYTE_T * item, A68G_REF ref_file)
1386  {
1387    CHECK_REF (p, ref_file, M_REF_FILE);
1388    A68G_FILE *f = FILE_DEREF (&ref_file);
1389    errno = 0;
1390    if (mode == M_PROC_REF_FILE_VOID) {
1391      genie_call_proc_ref_file_void (p, ref_file, *(A68G_PROCEDURE *) item);
1392    } else if (mode == M_FORMAT) {
1393      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_UNDEFINED_TRANSPUT, M_FORMAT);
1394      exit_genie (p, A68G_RUNTIME_ERROR);
1395    } else if (mode == M_SOUND) {
1396      write_sound (p, ref_file, (A68G_SOUND *) item);
1397    } else if (mode == M_INT) {
1398      ASSERT (io_write (FD (f), &(VALUE ((A68G_INT *) item)), sizeof (VALUE ((A68G_INT *) item))) != -1);
1399    } else if (mode == M_LONG_INT) {
1400  #if (A68G_LEVEL >= 3)
1401      ASSERT (io_write (FD (f), &(VALUE ((A68G_LONG_INT *) item)), sizeof (VALUE ((A68G_LONG_INT *) item))) != -1);
1402  #else
1403      ASSERT (io_write (FD (f), (MP_T *) item, (size_t) SIZE (mode)) != -1);
1404  #endif
1405    } else if (mode == M_LONG_LONG_INT) {
1406      ASSERT (io_write (FD (f), (MP_T *) item, (size_t) SIZE (mode)) != -1);
1407    } else if (mode == M_REAL) {
1408      ASSERT (io_write (FD (f), &(VALUE ((A68G_REAL *) item)), sizeof (VALUE ((A68G_REAL *) item))) != -1);
1409    } else if (mode == M_LONG_REAL) {
1410  #if (A68G_LEVEL >= 3)
1411      ASSERT (io_write (FD (f), &(VALUE ((A68G_LONG_REAL *) item)), sizeof (VALUE ((A68G_LONG_REAL *) item))) != -1);
1412  #else
1413      ASSERT (io_write (FD (f), (MP_T *) item, (size_t) SIZE (mode)) != -1);
1414  #endif
1415    } else if (mode == M_LONG_LONG_REAL) {
1416      ASSERT (io_write (FD (f), (MP_T *) item, (size_t) SIZE (mode)) != -1);
1417    } else if (mode == M_BOOL) {
1418      ASSERT (io_write (FD (f), &(VALUE ((A68G_BOOL *) item)), sizeof (VALUE ((A68G_BOOL *) item))) != -1);
1419    } else if (mode == M_CHAR) {
1420      ASSERT (io_write (FD (f), &(VALUE ((A68G_CHAR *) item)), sizeof (VALUE ((A68G_CHAR *) item))) != -1);
1421    } else if (mode == M_BITS) {
1422      ASSERT (io_write (FD (f), &(VALUE ((A68G_BITS *) item)), sizeof (VALUE ((A68G_BITS *) item))) != -1);
1423    } else if (mode == M_LONG_BITS) {
1424  #if (A68G_LEVEL >= 3)
1425      ASSERT (io_write (FD (f), &(VALUE ((A68G_LONG_BITS *) item)), sizeof (VALUE ((A68G_LONG_BITS *) item))) != -1);
1426  #else
1427      ASSERT (io_write (FD (f), (MP_T *) item, (size_t) SIZE (mode)) != -1);
1428  #endif
1429    } else if (mode == M_LONG_LONG_BITS) {
1430      ASSERT (io_write (FD (f), (MP_T *) item, (size_t) SIZE (mode)) != -1);
1431    } else if (mode == M_ROW_CHAR || mode == M_STRING) {
1432      reset_transput_buffer (UNFORMATTED_BUFFER);
1433      add_a_string_transput_buffer (p, UNFORMATTED_BUFFER, item);
1434      int len = get_transput_buffer_index (UNFORMATTED_BUFFER);
1435      ASSERT (io_write (FD (f), &(len), sizeof (len)) != -1);
1436      WRITE (FD (f), get_transput_buffer (UNFORMATTED_BUFFER));
1437    } else if (IS_UNION (mode)) {
1438      A68G_UNION *z = (A68G_UNION *) item;
1439      genie_write_bin_standard (p, (MOID_T *) (VALUE (z)), &item[A68G_UNION_SIZE], ref_file);
1440    } else if (IS_STRUCT (mode)) {
1441      for (PACK_T *q = PACK (mode); q != NO_PACK; FORWARD (q)) {
1442        BYTE_T *elem = &item[OFFSET (q)];
1443        genie_check_initialisation (p, elem, MOID (q));
1444        genie_write_bin_standard (p, MOID (q), elem, ref_file);
1445      }
1446    } else if (IS_ROW (mode) || IS_FLEX (mode)) {
1447      MOID_T *deflexed = DEFLEX (mode);
1448      A68G_ARRAY *arr; A68G_TUPLE *tup;
1449      CHECK_INIT (p, INITIALISED ((A68G_REF *) item), M_ROWS);
1450      GET_DESCRIPTOR (arr, tup, (A68G_REF *) item);
1451      if (get_row_size (tup, DIM (arr)) > 0) {
1452        BYTE_T *base_addr = DEREF (BYTE_T, &ARRAY (arr));
1453        BOOL_T done = A68G_FALSE;
1454        initialise_internal_index (tup, DIM (arr));
1455        while (!done) {
1456          ADDR_T a68g_index = calculate_internal_index (tup, DIM (arr));
1457          ADDR_T elem_addr = ROW_ELEMENT (arr, a68g_index);
1458          BYTE_T *elem = &base_addr[elem_addr];
1459          genie_check_initialisation (p, elem, SUB (deflexed));
1460          genie_write_bin_standard (p, SUB (deflexed), elem, ref_file);
1461          done = increment_internal_index (tup, DIM (arr));
1462        }
1463      }
1464    }
1465    if (errno != 0) {
1466      transput_error (p, ref_file, mode);
1467    }
1468  }
1469  
1470  //! @brief PROC ([] SIMPLOUT) VOID write bin, print bin
1471  
1472  void genie_write_bin (NODE_T * p)
1473  {
1474    A68G_REF row;
1475    POP_REF (p, &row);
1476    genie_stand_back (p);
1477    PUSH_REF (p, row);
1478    genie_write_bin_file (p);
1479  }
1480  
1481  //! @brief PROC (REF FILE, [] SIMPLOUT) VOID put bin
1482  
1483  void genie_write_bin_file (NODE_T * p)
1484  {
1485    A68G_REF row; A68G_ARRAY *arr; A68G_TUPLE *tup;
1486    POP_REF (p, &row);
1487    CHECK_REF (p, row, M_ROW_SIMPLOUT);
1488    GET_DESCRIPTOR (arr, tup, &row);
1489    int elems = ROW_SIZE (tup);
1490    A68G_REF ref_file;
1491    POP_REF (p, &ref_file);
1492    ref_file = *(A68G_REF *) STACK_TOP;
1493    CHECK_REF (p, ref_file, M_REF_FILE);
1494    A68G_FILE *file = FILE_DEREF (&ref_file);
1495    CHECK_INIT (p, INITIALISED (file), M_FILE);
1496    if (!OPENED (file)) {
1497      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
1498      exit_genie (p, A68G_RUNTIME_ERROR);
1499    }
1500    if (DRAW_MOOD (file)) {
1501      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
1502      exit_genie (p, A68G_RUNTIME_ERROR);
1503    }
1504    if (READ_MOOD (file)) {
1505      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "read");
1506      exit_genie (p, A68G_RUNTIME_ERROR);
1507    }
1508    if (!PUT (&CHANNEL (file))) {
1509      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "putting");
1510      exit_genie (p, A68G_RUNTIME_ERROR);
1511    }
1512    if (!BIN (&CHANNEL (file))) {
1513      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "binary putting");
1514      exit_genie (p, A68G_RUNTIME_ERROR);
1515    }
1516    if (!READ_MOOD (file) && !WRITE_MOOD (file)) {
1517      if ((FD (file) = open_physical_file (p, ref_file, A68G_WRITE_ACCESS | O_BINARY, A68G_PROTECTION)) == A68G_NO_FILE) {
1518        open_error (p, ref_file, "binary putting");
1519      }
1520      DRAW_MOOD (file) = A68G_FALSE;
1521      READ_MOOD (file) = A68G_FALSE;
1522      WRITE_MOOD (file) = A68G_TRUE;
1523      CHAR_MOOD (file) = A68G_FALSE;
1524    }
1525    if (CHAR_MOOD (file)) {
1526      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "text");
1527      exit_genie (p, A68G_RUNTIME_ERROR);
1528    }
1529    if (elems <= 0) {
1530      return;
1531    }
1532    BYTE_T *base_address = DEREF (BYTE_T, &ARRAY (arr));
1533    int elem_index = 0;
1534    for (int k = 0; k < elems; k++) {
1535      A68G_UNION *z = (A68G_UNION *) & base_address[elem_index];
1536      MOID_T *mode = (MOID_T *) (VALUE (z));
1537      BYTE_T *item = (BYTE_T *) & base_address[elem_index + A68G_UNION_SIZE];
1538      genie_write_bin_standard (p, mode, item, ref_file);
1539      elem_index += SIZE (M_SIMPLOUT);
1540    }
1541  }
1542  
1543  // Next are formatting routines "whole", "fixed" and "float" for mode
1544  // INT, LONG INT and LONG LONG INT, and REAL, LONG REAL and LONG LONG REAL.
1545  // They are direct implementations of the routines described in the
1546  // Revised Report, although those were only meant as a specification.
1547  
1548  //! @brief Generate a string of error chars.
1549  
1550  char *error_chars (char *s, int n)
1551  {
1552    int k = (n != 0 ? ABS (n) : 1);
1553    s[k] = NULL_CHAR;
1554    while (--k >= 0) {
1555      s[k] = ERROR_CHAR;
1556    }
1557    return s;
1558  }
1559  
1560  //! @brief Convert temporary C string to A68 string.
1561  
1562  A68G_REF tmp_to_a68g_string (NODE_T * p, char *temp_string)
1563  {
1564  // no compaction allowed since temp_string might be up for garbage collecting ...
1565    return c_to_a_string (p, temp_string, DEFAULT_WIDTH);
1566  }
1567  
1568  //! @brief Add c to str, assuming that "str" is large enough.
1569  
1570  char *plusto (char c, char *str)
1571  {
1572    MOVE (&str[1], &str[0], strlen (str) + 1);
1573    str[0] = c;
1574    return str;
1575  }
1576  
1577  //! @brief Add c to str, assuming that "str" is large enough.
1578  
1579  char *string_plusab_char (char *str, char c, int strwid)
1580  {
1581    char z[2];
1582    z[0] = c;
1583    z[1] = NULL_CHAR;
1584    a68g_bufcat (str, z, strwid);
1585    return str;
1586  }
1587  
1588  //! @brief Add leading spaces to str until length is width.
1589  
1590  char *leading_spaces (char *str, int width)
1591  {
1592    int j = width - strlen (str);
1593    while (--j >= 0) {
1594      (void) plusto (BLANK_CHAR, str);
1595    }
1596    return str;
1597  }
1598  
1599  //! @brief Convert int to char using a table.
1600  
1601  char digchar (int k)
1602  {
1603    char *s = "0123456789abcdefghijklmnopqrstuvwxyz";
1604    if (k >= 0 && k < strlen (s)) {
1605      return s[k];
1606    } else {
1607      return ERROR_CHAR;
1608    }
1609  }
1610  
1611  //! @brief Formatted string for HEX_NUMBER.
1612  
1613  char *bits_to_string (NODE_T * p)
1614  {
1615    A68G_INT width, base;
1616    POP_OBJECT (p, &base, A68G_INT);
1617    POP_OBJECT (p, &width, A68G_INT);
1618    DECREMENT_STACK_POINTER (p, SIZE (M_HEX_NUMBER));
1619    CHECK_INT_SHORTEN (p, VALUE (&base));
1620    CHECK_INT_SHORTEN (p, VALUE (&width));
1621    MOID_T *mode = (MOID_T *) (VALUE ((A68G_UNION *) STACK_TOP));
1622    ADDR_T pop_sp = A68G_SP;
1623    int length = ABS (VALUE (&width)), radix = ABS (VALUE (&base));
1624    if (radix < 2 || radix > 16) {
1625      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_INVALID_RADIX, radix);
1626      exit_genie (p, A68G_RUNTIME_ERROR);
1627    }
1628    reset_transput_buffer (EDIT_BUFFER);
1629  #if (A68G_LEVEL <= 2)
1630    (void) mode;
1631    (void) length;
1632    (void) error_chars (get_transput_buffer (EDIT_BUFFER), VALUE (&width));
1633  #else
1634    {
1635      BOOL_T ret = A68G_TRUE;
1636      if (mode == M_BOOL) {
1637        UNSIGNED_T z = VALUE ((A68G_BOOL *) (STACK_OFFSET (A68G_UNION_SIZE)));
1638        ret = convert_radix (p, (UNSIGNED_T) z, radix, length);
1639      } else if (mode == M_CHAR) {
1640        INT_T z = VALUE ((A68G_CHAR *) (STACK_OFFSET (A68G_UNION_SIZE)));
1641        ret = convert_radix (p, (UNSIGNED_T) z, radix, length);
1642      } else if (mode == M_INT) {
1643        INT_T z = VALUE ((A68G_INT *) (STACK_OFFSET (A68G_UNION_SIZE)));
1644        ret = convert_radix (p, (UNSIGNED_T) z, radix, length);
1645      } else if (mode == M_REAL) {
1646  // A trick to copy a REAL into an unt without truncating
1647        UNSIGNED_T z;
1648        memcpy (&z, (void *) &VALUE ((A68G_REAL *) (STACK_OFFSET (A68G_UNION_SIZE))), 8);
1649        ret = convert_radix (p, z, radix, length);
1650      } else if (mode == M_BITS) {
1651        UNSIGNED_T z = VALUE ((A68G_BITS *) (STACK_OFFSET (A68G_UNION_SIZE)));
1652        ret = convert_radix (p, (UNSIGNED_T) z, radix, length);
1653      } else if (mode == M_LONG_INT) {
1654        DOUBLE_NUM_T z = VALUE ((A68G_LONG_INT *) (STACK_OFFSET (A68G_UNION_SIZE)));
1655        ret = convert_radix_double (p, z, radix, length);
1656      } else if (mode == M_LONG_REAL) {
1657        DOUBLE_NUM_T z = VALUE ((A68G_LONG_REAL *) (STACK_OFFSET (A68G_UNION_SIZE)));
1658        ret = convert_radix_double (p, z, radix, length);
1659      } else if (mode == M_LONG_BITS) {
1660        DOUBLE_NUM_T z = VALUE ((A68G_LONG_BITS *) (STACK_OFFSET (A68G_UNION_SIZE)));
1661        ret = convert_radix_double (p, z, radix, length);
1662      }
1663      if (ret == A68G_FALSE) {
1664        errno = EDOM;
1665        PRELUDE_ERROR (A68G_TRUE, p, ERROR_OUT_OF_BOUNDS, mode);
1666      }
1667    }
1668  #endif
1669    A68G_SP = pop_sp;
1670    return get_transput_buffer (EDIT_BUFFER);
1671  }
1672  
1673  //! @brief Standard string for LONG INT.
1674  
1675  char *sub_whole_mp (NODE_T * p, MP_T * m, int digits, int width)
1676  {
1677    int len = 0;
1678    char *s = stack_string (p, 8 + width);
1679    s[0] = NULL_CHAR;
1680    ADDR_T pop_sp = A68G_SP;
1681    MP_T *n = nil_mp (p, digits);
1682    (void) move_mp (n, m, digits);
1683    do {
1684      if (len < width) {
1685  // Sic transit gloria mundi.
1686        int n_mod_10 = (MP_INT_T) MP_DIGIT (n, (int) (1 + MP_EXPONENT (n))) % 10;
1687        (void) plusto (digchar (n_mod_10), s);
1688      }
1689      len++;
1690      (void) over_mp_digit (p, n, n, (MP_T) 10, digits);
1691    } while (MP_DIGIT (n, 1) > 0);
1692    if (len > width) {
1693      (void) error_chars (s, width);
1694    }
1695    A68G_SP = pop_sp;
1696    return s;
1697  }
1698  
1699  //! @brief Formatted string for NUMBER.
1700  
1701  char *whole (NODE_T * p)
1702  {
1703    A68G_INT width;
1704    POP_OBJECT (p, &width, A68G_INT);
1705    CHECK_INT_SHORTEN (p, VALUE (&width));
1706    DECREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
1707    ADDR_T pop_sp = A68G_SP;
1708    MOID_T *mode = (MOID_T *) (VALUE ((A68G_UNION *) STACK_TOP));
1709  //
1710    if (mode == M_REAL || mode == M_LONG_REAL || mode == M_LONG_LONG_REAL) {
1711      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
1712      PUSH_VALUE (p, VALUE (&width), A68G_INT);
1713      PUSH_VALUE (p, 0, A68G_INT);
1714      return fixed (p);
1715    } else if (mode == M_INT) {
1716      INT_T x = VALUE ((A68G_INT *) (STACK_OFFSET (A68G_UNION_SIZE)));
1717      int digits = DIGITS (M_LONG_LONG_INT);
1718      PUSH_UNION (p, (void *) M_LONG_LONG_INT);
1719      MP_T *z = nil_mp (p, digits);
1720      (void) int_to_mp (p, z, x, digits);
1721      PUSH_PRIMAL (p, VALUE (&width), INT);
1722      return whole (p);
1723    }
1724  #if (A68G_LEVEL >= 3)
1725    if (mode == M_LONG_INT) {
1726      DOUBLE_NUM_T x = VALUE ((A68G_LONG_INT *) (STACK_OFFSET (A68G_UNION_SIZE)));
1727      int digits = DIGITS (M_LONG_LONG_INT);
1728      PUSH_UNION (p, (void *) M_LONG_LONG_INT);
1729      MP_T *z = nil_mp (p, digits);
1730      (void) double_int_to_mp (p, z, x, digits);
1731      PUSH_PRIMAL (p, VALUE (&width), INT);
1732      return whole (p);
1733    }
1734  #endif
1735    if (mode == M_LONG_INT || mode == M_LONG_LONG_INT) {
1736      int digits = DIGITS (mode);
1737      MP_T *n = (MP_T *) (STACK_OFFSET (A68G_UNION_SIZE));
1738      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
1739      if (MP_EXPONENT (n) >= (MP_T) digits) {
1740        int max_length = (mode == M_LONG_INT ? A68G_LONG_INT_WIDTH : A68G_LONG_LONG_INT_WIDTH);
1741        int length = (VALUE (&width) == 0 ? max_length : VALUE (&width));
1742        char *s = stack_string (p, 1 + length);
1743        (void) error_chars (s, length);
1744        A68G_SP = pop_sp;
1745        return s;
1746      }
1747      BOOL_T ltz = (BOOL_T) (MP_DIGIT (n, 1) < 0);
1748      int length = ABS (VALUE (&width)) - (ltz || VALUE (&width) > 0 ? 1 : 0);
1749      size_t size = (ltz ? 1 : (VALUE (&width) > 0 ? 1 : 0));
1750      MP_DIGIT (n, 1) = ABS (MP_DIGIT (n, 1));
1751      if (VALUE (&width) == 0) {
1752        MP_T *m = nil_mp (p, digits);
1753        (void) move_mp (m, n, digits);
1754        length = 0;
1755        while ((over_mp_digit (p, m, m, (MP_T) 10, digits), length++, MP_DIGIT (m, 1) != 0)) {
1756          ;
1757        }
1758      }
1759      size += length;
1760      int abs_width = ABS (VALUE (&width));
1761      size = 8 + MAX (size, abs_width);
1762      char *s = stack_string (p, size);
1763      a68g_bufcpy (s, sub_whole_mp (p, n, digits, length), size);
1764      if (length == 0 || strchr (s, ERROR_CHAR) != NO_TEXT) {
1765        (void) error_chars (s, abs_width);
1766      } else {
1767        if (ltz) {
1768          (void) plusto ('-', s);
1769        } else if (VALUE (&width) > 0) {
1770          (void) plusto ('+', s);
1771        }
1772        if (VALUE (&width) != 0) {
1773          (void) leading_spaces (s, abs_width);
1774        }
1775      }
1776      A68G_SP = pop_sp;
1777      return s;
1778    }
1779    ABEND (A68G_TRUE, ERROR_INTERNAL_CONSISTENCY, __func__);
1780    return NO_TEXT;
1781  }
1782  
1783  //! @brief Fetch next digit from LONG.
1784  
1785  char choose_dig_mp (NODE_T * p, MP_T * y, int digits)
1786  {
1787  // Assuming positive "y".
1788    ADDR_T pop_sp = A68G_SP;
1789    (void) mul_mp_digit (p, y, y, (MP_T) 10, digits);
1790    int c = MP_EXPONENT (y) == 0 ? (MP_INT_T) MP_DIGIT (y, 1) : 0;
1791    if (c > 9) {
1792      c = 9;
1793    }
1794    MP_T *t = lit_mp (p, c, 0, digits);
1795    (void) sub_mp (p, y, y, t, digits);
1796  // Reset the stack to prevent overflow, there may be many digits.
1797    A68G_SP = pop_sp;
1798    return digchar (c);
1799  }
1800  
1801  //! @brief Standard string for LONG.
1802  
1803  char *sub_fixed_mp (NODE_T * p, MP_T * x, int digits, int width, int after)
1804  {
1805    ADDR_T pop_sp = A68G_SP;
1806    MP_T *y = nil_mp (p, digits);
1807    MP_T *s = nil_mp (p, digits);
1808    MP_T *t = nil_mp (p, digits);
1809    (void) ten_up_mp (p, t, -after, digits);
1810    (void) half_mp (p, t, t, digits);
1811    (void) add_mp (p, y, x, t, digits);
1812    int before = 0;
1813  // Not RR - argument reduction.
1814    while (MP_EXPONENT (y) > 1) {
1815      int k = (int) round (MP_EXPONENT (y) - 1);
1816      MP_EXPONENT (y) -= k;
1817      before += k * LOG_MP_RADIX;
1818    }
1819  // Follow RR again.
1820    SET_MP_ONE (s, digits);
1821    while ((sub_mp (p, t, y, s, digits), MP_DIGIT (t, 1) >= 0)) {
1822      before++;
1823      (void) div_mp_digit (p, y, y, (MP_T) 10, digits);
1824    }
1825  // Compose the number.
1826    if (before + after + (after > 0 ? 1 : 0) > width) {
1827      char *str = stack_string (p, width + 1);
1828      (void) error_chars (str, width);
1829      A68G_SP = pop_sp;
1830      return str;
1831    }
1832    int strwid = 8 + before + after;
1833    char *str = stack_string (p, strwid);
1834    str[0] = NULL_CHAR;
1835    int len = 0;
1836    for (int j = 0; j < before; j++) {
1837      char ch = (char) (len < A68G_LONG_LONG_REAL_WIDTH ? choose_dig_mp (p, y, digits) : '0');
1838      (void) string_plusab_char (str, ch, strwid);
1839      len++;
1840    }
1841    if (after > 0) {
1842      (void) string_plusab_char (str, POINT_CHAR, strwid);
1843    }
1844    for (int j = 0; j < after; j++) {
1845      char ch = (char) (len < A68G_LONG_LONG_REAL_WIDTH ? choose_dig_mp (p, y, digits) : '0');
1846      (void) string_plusab_char (str, ch, strwid);
1847      len++;
1848    }
1849    if (strlen (str) > width) {
1850      (void) error_chars (str, width);
1851    }
1852    A68G_SP = pop_sp;
1853    return str;
1854  }
1855  
1856  //! @brief Formatted string for NUMBER.
1857  
1858  char *fixed (NODE_T * p)
1859  {
1860    A68G_INT width, after;
1861    POP_OBJECT (p, &after, A68G_INT);
1862    POP_OBJECT (p, &width, A68G_INT);
1863    CHECK_INT_SHORTEN (p, VALUE (&after));
1864    CHECK_INT_SHORTEN (p, VALUE (&width));
1865    DECREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
1866    ADDR_T pop_sp = A68G_SP;
1867    MOID_T *mode = (MOID_T *) (VALUE ((A68G_UNION *) STACK_TOP));
1868    if (mode == M_INT) {
1869      INT_T k = VALUE ((A68G_INT *) (STACK_OFFSET (A68G_UNION_SIZE)));
1870      PUSH_UNION (p, M_REAL);
1871      PUSH_VALUE (p, (REAL_T) k, A68G_REAL);
1872      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68G_UNION_SIZE + SIZE (M_REAL)));
1873      PUSH_VALUE (p, VALUE (&width), A68G_INT);
1874      PUSH_VALUE (p, VALUE (&after), A68G_INT);
1875      return fixed (p);
1876    } else if (mode == M_REAL) {
1877      REAL_T x = VALUE ((A68G_REAL *) (STACK_OFFSET (A68G_UNION_SIZE)));
1878      int digits = DIGITS (M_LONG_LONG_REAL);
1879      PUSH_UNION (p, (void *) M_LONG_LONG_REAL);
1880      MP_T *z = nil_mp (p, digits);
1881  #if (A68G_LEVEL >= 3)
1882      (void) double_to_mp (p, z, (DOUBLE_T) x, A68G_REAL_DIG, A68G_TRUE, digits);
1883  #else
1884      (void) real_to_mp (p, z, x, digits);
1885  #endif
1886      PUSH_PRIMAL (p, VALUE (&width), INT);
1887      PUSH_PRIMAL (p, VALUE (&after), INT);
1888      return fixed (p);
1889    }
1890  #if (A68G_LEVEL >= 3)
1891    if (mode == M_LONG_INT) {
1892      DOUBLE_NUM_T x = VALUE ((A68G_LONG_INT *) (STACK_OFFSET (A68G_UNION_SIZE)));
1893      int digits = DIGITS (M_LONG_LONG_REAL);
1894      PUSH_UNION (p, (void *) M_LONG_LONG_REAL);
1895      MP_T *z = nil_mp (p, digits);
1896      (void) double_int_to_mp (p, z, x, digits);
1897      PUSH_PRIMAL (p, VALUE (&width), INT);
1898      PUSH_PRIMAL (p, VALUE (&after), INT);
1899      return fixed (p);
1900    } else if (mode == M_LONG_REAL) {
1901      DOUBLE_T x = VALUE ((A68G_LONG_REAL *) (STACK_OFFSET (A68G_UNION_SIZE))).f;
1902      CHECK_DOUBLE_REAL (p, x);
1903      int digits = DIGITS (M_LONG_LONG_REAL);
1904      PUSH_UNION (p, (void *) M_LONG_LONG_REAL);
1905      MP_T *z = nil_mp (p, digits);
1906      (void) double_to_mp (p, z, x, A68G_DOUBLE_DIG, A68G_TRUE, digits);
1907      PUSH_PRIMAL (p, VALUE (&width), INT);
1908      PUSH_PRIMAL (p, VALUE (&after), INT);
1909      return fixed (p);
1910    }
1911  #endif
1912    if (mode == M_LONG_INT || mode == M_LONG_LONG_INT) {
1913      if (mode == M_LONG_INT) {
1914        VALUE ((A68G_UNION *) STACK_TOP) = (void *) M_LONG_REAL;
1915      } else {
1916        VALUE ((A68G_UNION *) STACK_TOP) = (void *) M_LONG_LONG_REAL;
1917      } 
1918      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
1919      PUSH_VALUE (p, VALUE (&width), A68G_INT);
1920      PUSH_VALUE (p, VALUE (&after), A68G_INT);
1921      return fixed (p);
1922    } else if (mode == M_LONG_REAL || mode == M_LONG_LONG_REAL) {
1923      int digits = DIGITS (mode);
1924      MP_T *x = (MP_T *) (STACK_OFFSET (A68G_UNION_SIZE));
1925      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
1926      BOOL_T ltz = (BOOL_T) (MP_DIGIT (x, 1) < 0);
1927      MP_DIGIT (x, 1) = ABS (MP_DIGIT (x, 1));
1928      int length = ABS (VALUE (&width)) - (ltz || VALUE (&width) > 0 ? 1 : 0);
1929      if (VALUE (&after) >= 0 && (length > VALUE (&after) || VALUE (&width) == 0)) {
1930        MP_T *z0 = nil_mp (p, digits);
1931        MP_T *z1 = nil_mp (p, digits);
1932        MP_T *t = nil_mp (p, digits);
1933        if (VALUE (&width) == 0) {
1934          length = (VALUE (&after) == 0 ? 1 : 0);
1935          (void) set_mp (z0, (MP_T) (MP_RADIX / 10), -1, digits);
1936          (void) set_mp (z1, (MP_T) 10, 0, digits);
1937          (void) pow_mp_int (p, z0, z0, VALUE (&after), digits);
1938          (void) pow_mp_int (p, z1, z1, length, digits);
1939          while ((div_mp_digit (p, t, z0, (MP_T) 2, digits), add_mp (p, t, x, t, digits), sub_mp (p, t, t, z1, digits), MP_DIGIT (t, 1) > 0)) {
1940            length++;
1941            (void) mul_mp_digit (p, z1, z1, (MP_T) 10, digits);
1942          }
1943          length += (VALUE (&after) == 0 ? 0 : VALUE (&after) + 1);
1944        }
1945        char *s = sub_fixed_mp (p, x, digits, length, VALUE (&after));
1946        if (strchr (s, ERROR_CHAR) == NO_TEXT) {
1947          if (length > strlen (s) && (s[0] != NULL_CHAR ? s[0] == POINT_CHAR : A68G_TRUE) && (MP_EXPONENT (x) < 0 || MP_DIGIT (x, 1) == 0)) {
1948            (void) plusto ('0', s);
1949          }
1950          if (ltz) {
1951            (void) plusto ('-', s);
1952          } else if (VALUE (&width) > 0) {
1953            (void) plusto ('+', s);
1954          }
1955          if (VALUE (&width) != 0) {
1956            (void) leading_spaces (s, ABS (VALUE (&width)));
1957          }
1958          A68G_SP = pop_sp;
1959          return s;
1960        } else if (VALUE (&after) > 0) {
1961          A68G_SP = pop_sp;
1962          MP_DIGIT (x, 1) = ltz ? -ABS (MP_DIGIT (x, 1)) : ABS (MP_DIGIT (x, 1));
1963          INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
1964          PUSH_VALUE (p, VALUE (&width), A68G_INT);
1965          PUSH_VALUE (p, VALUE (&after) - 1, A68G_INT);
1966          return fixed (p);
1967        } else {
1968          A68G_SP = pop_sp;
1969          return error_chars (s, VALUE (&width));
1970        }
1971      } else {
1972        char *s = stack_string (p, 8 + ABS (VALUE (&width)));
1973        A68G_SP = pop_sp;
1974        return error_chars (s, VALUE (&width));
1975      }
1976    }
1977    ABEND (A68G_TRUE, ERROR_INTERNAL_CONSISTENCY, __func__);
1978    return NO_TEXT;
1979  }
1980  
1981  //! @brief Scale LONG for formatting.
1982  
1983  void standardize_mp (NODE_T * p, MP_T * y, int digits, int before, int after, int *q)
1984  {
1985    ADDR_T pop_sp = A68G_SP;
1986    MP_T *f = nil_mp (p, digits);
1987    MP_T *g = nil_mp (p, digits);
1988    MP_T *h = nil_mp (p, digits);
1989    MP_T *t = nil_mp (p, digits);
1990    ten_up_mp (p, g, before, digits);
1991    (void) div_mp_digit (p, h, g, (MP_T) 10, digits);
1992  // Speed huge exponents.
1993    if ((MP_EXPONENT (y) - MP_EXPONENT (g)) > 1) {
1994      (*q) += LOG_MP_RADIX * ((int) MP_EXPONENT (y) - (int) MP_EXPONENT (g) - 1);
1995      MP_EXPONENT (y) = MP_EXPONENT (g) + 1;
1996    }
1997    while ((sub_mp (p, t, y, g, digits), MP_DIGIT (t, 1) >= 0)) {
1998      (void) div_mp_digit (p, y, y, (MP_T) 10, digits);
1999      (*q)++;
2000    }
2001    if (MP_DIGIT (y, 1) != 0) {
2002  // Speed huge exponents.
2003      if ((MP_EXPONENT (y) - MP_EXPONENT (h)) < -1) {
2004        (*q) -= LOG_MP_RADIX * ((int) MP_EXPONENT (h) - (int) MP_EXPONENT (y) - 1);
2005        MP_EXPONENT (y) = MP_EXPONENT (h) - 1;
2006      }
2007      while ((sub_mp (p, t, y, h, digits), MP_DIGIT (t, 1) < 0)) {
2008        (void) mul_mp_digit (p, y, y, (MP_T) 10, digits);
2009        (*q)--;
2010      }
2011    }
2012    ten_up_mp (p, f, -after, digits);
2013    (void) div_mp_digit (p, t, f, (MP_T) 2, digits);
2014    (void) add_mp (p, t, y, t, digits);
2015    (void) sub_mp (p, t, t, g, digits);
2016    if (MP_DIGIT (t, 1) >= 0) {
2017      (void) move_mp (y, h, digits);
2018      (*q)++;
2019    }
2020    A68G_SP = pop_sp;
2021  }
2022  
2023  //! @brief Formatted string for NUMBER.
2024  
2025  char *real (NODE_T * p)
2026  {
2027  // POP arguments.
2028    A68G_INT width, after, expo, frmt;
2029    POP_OBJECT (p, &frmt, A68G_INT);
2030    POP_OBJECT (p, &expo, A68G_INT);
2031    POP_OBJECT (p, &after, A68G_INT);
2032    POP_OBJECT (p, &width, A68G_INT);
2033    CHECK_INT_SHORTEN (p, VALUE (&frmt));
2034    CHECK_INT_SHORTEN (p, VALUE (&expo));
2035    CHECK_INT_SHORTEN (p, VALUE (&after));
2036    CHECK_INT_SHORTEN (p, VALUE (&width));
2037    ADDR_T arg_sp = A68G_SP;
2038    DECREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
2039    MOID_T *mode = (MOID_T *) (VALUE ((A68G_UNION *) STACK_TOP));
2040    ADDR_T pop_sp = A68G_SP;
2041  //
2042    if (mode == M_INT) {
2043      INT_T k = VALUE ((A68G_INT *) (STACK_OFFSET (A68G_UNION_SIZE)));
2044      PUSH_UNION (p, (void *) M_LONG_LONG_REAL);
2045      int digits = DIGITS (M_LONG_LONG_REAL);
2046      MP_T *z = nil_mp (p, digits);
2047      int_to_mp (p, z, k, digits);
2048      PUSH_VALUE (p, VALUE (&width), A68G_INT);
2049      PUSH_VALUE (p, VALUE (&after), A68G_INT);
2050      PUSH_VALUE (p, VALUE (&expo), A68G_INT);
2051      PUSH_VALUE (p, VALUE (&frmt), A68G_INT);
2052      return real (p);
2053    } else if (mode == M_REAL) {
2054      REAL_T x = VALUE ((A68G_REAL *) (STACK_OFFSET (A68G_UNION_SIZE)));
2055      PUSH_UNION (p, (void *) M_LONG_LONG_REAL);
2056      int digits = DIGITS (M_LONG_LONG_REAL);
2057      MP_T *z = nil_mp (p, digits);
2058  #if (A68G_LEVEL >= 3)
2059      (void) double_to_mp (p, z, (DOUBLE_T) x, A68G_REAL_DIG, A68G_TRUE, digits);
2060  #else
2061      (void) real_to_mp (p, z, x, digits);
2062  #endif
2063      PUSH_PRIMAL (p, VALUE (&width), INT);
2064      PUSH_PRIMAL (p, VALUE (&after), INT);
2065      PUSH_PRIMAL (p, VALUE (&expo), INT);
2066      PUSH_PRIMAL (p, VALUE (&frmt), INT);
2067      return real (p);
2068    }
2069  #if (A68G_LEVEL >= 3)
2070    if (mode == M_LONG_INT) {
2071      DOUBLE_NUM_T k = VALUE ((A68G_LONG_REAL *) (STACK_OFFSET (A68G_UNION_SIZE)));
2072      int digits = DIGITS (M_LONG_LONG_REAL);
2073      PUSH_UNION (p, (void *) M_LONG_LONG_REAL);
2074      MP_T *z = nil_mp (p, digits);
2075      (void) double_int_to_mp (p, z, k, digits);
2076      PUSH_PRIMAL (p, VALUE (&width), INT);
2077      PUSH_PRIMAL (p, VALUE (&after), INT);
2078      PUSH_PRIMAL (p, VALUE (&expo), INT);
2079      PUSH_PRIMAL (p, VALUE (&frmt), INT);
2080      return real (p);
2081    } else if (mode == M_LONG_REAL) {
2082      DOUBLE_T x = VALUE ((A68G_LONG_REAL *) (STACK_OFFSET (A68G_UNION_SIZE))).f;
2083      CHECK_DOUBLE_REAL (p, x);
2084      int digits = DIGITS (M_LONG_LONG_REAL);
2085      PUSH_UNION (p, (void *) M_LONG_LONG_REAL);
2086      MP_T *z = nil_mp (p, digits);
2087      (void) double_to_mp (p, z, (DOUBLE_T) x, A68G_DOUBLE_DIG, A68G_TRUE, digits);
2088      PUSH_PRIMAL (p, VALUE (&width), INT);
2089      PUSH_PRIMAL (p, VALUE (&after), INT);
2090      PUSH_PRIMAL (p, VALUE (&expo), INT);
2091      PUSH_PRIMAL (p, VALUE (&frmt), INT);
2092      return real (p);
2093    }
2094  #endif
2095    if (mode == M_LONG_INT || mode == M_LONG_LONG_INT) {
2096      A68G_SP = pop_sp;
2097      if (mode == M_LONG_INT) {
2098        VALUE ((A68G_UNION *) STACK_TOP) = (void *) M_LONG_REAL;
2099      } else {
2100        VALUE ((A68G_UNION *) STACK_TOP) = (void *) M_LONG_LONG_REAL;
2101      } 
2102      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
2103      PUSH_VALUE (p, VALUE (&width), A68G_INT);
2104      PUSH_VALUE (p, VALUE (&after), A68G_INT);
2105      PUSH_VALUE (p, VALUE (&expo), A68G_INT);
2106      PUSH_VALUE (p, VALUE (&frmt), A68G_INT);
2107      return real (p);
2108    } else if (mode == M_LONG_REAL || mode == M_LONG_LONG_REAL) {
2109      int digits = DIGITS (mode);
2110      int before;
2111      MP_T *x = (MP_T *) (STACK_OFFSET (A68G_UNION_SIZE));
2112      CHECK_LONG_REAL (p, x, mode);
2113      BOOL_T ltz = (BOOL_T) (MP_DIGIT (x, 1) < 0);
2114      A68G_SP = arg_sp;
2115      MP_DIGIT (x, 1) = ABS (MP_DIGIT (x, 1));
2116      before = ABS (VALUE (&width)) - ABS (VALUE (&expo)) - (VALUE (&after) != 0 ? VALUE (&after) + 1 : 0) - 2;
2117      if (SIGN (before) + SIGN (VALUE (&after)) > 0) {
2118        int q = 0;
2119        size_t N_mp = SIZE_MP (digits);
2120        MP_T *z = nil_mp (p, digits);
2121        (void) move_mp (z, x, digits);
2122        standardize_mp (p, z, digits, before, VALUE (&after), &q);
2123        if (VALUE (&frmt) > 0) {
2124          while (q % VALUE (&frmt) != 0) {
2125            (void) mul_mp_digit (p, z, z, (MP_T) 10, digits);
2126            q--;
2127            if (VALUE (&after) > 0) {
2128              VALUE (&after)--;
2129            }
2130          }
2131        } else {
2132          ADDR_T sp1 = A68G_SP;
2133          MP_T *dif = nil_mp (p, digits);
2134          MP_T *lim = nil_mp (p, digits);
2135          (void) ten_up_mp (p, lim, -VALUE (&frmt) - 1, digits);
2136          (void) sub_mp (p, dif, z, lim, digits);
2137          while (MP_DIGIT (dif, 1) < 0) {
2138            (void) mul_mp_digit (p, z, z, (MP_T) 10, digits);
2139            q--;
2140            if (VALUE (&after) > 0) {
2141              VALUE (&after)--;
2142            }
2143            (void) sub_mp (p, dif, z, lim, digits);
2144          }
2145          (void) mul_mp_digit (p, lim, lim, (MP_T) 10, digits);
2146          (void) sub_mp (p, dif, z, lim, digits);
2147          while (MP_DIGIT (dif, 1) > 0) {
2148            (void) div_mp_digit (p, z, z, (MP_T) 10, digits);
2149            q++;
2150            if (VALUE (&after) > 0) {
2151              VALUE (&after)++;
2152            }
2153            (void) sub_mp (p, dif, z, lim, digits);
2154          }
2155          A68G_SP = sp1;
2156        }
2157  //
2158        int strwid = 8 + ABS (VALUE (&width));
2159        char *s = stack_string (p, strwid);
2160  // Mantissa.
2161        PUSH_UNION (p, mode);
2162        MP_DIGIT (z, 1) = (ltz ? -MP_DIGIT (z, 1) : MP_DIGIT (z, 1));
2163        PUSH (p, z, N_mp);
2164        INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68G_UNION_SIZE + SIZE_MP (digits)));
2165        PUSH_VALUE (p, SIGN (VALUE (&width)) * (ABS (VALUE (&width)) - ABS (VALUE (&expo)) - 1), A68G_INT);
2166        PUSH_VALUE (p, VALUE (&after), A68G_INT);
2167        a68g_bufcpy (s, fixed (p), strwid);
2168  // Exponent.
2169        PUSH_UNION (p, M_INT);
2170        PUSH_VALUE (p, q, A68G_INT);
2171        INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68G_UNION_SIZE + SIZE (M_INT)));
2172        PUSH_VALUE (p, VALUE (&expo), A68G_INT);
2173        (void) string_plusab_char (s, EXPONENT_CHAR, strwid);
2174        a68g_bufcat (s, whole (p), strwid);
2175  // Recursion when error chars.
2176        if (VALUE (&expo) == 0 || strchr (s, ERROR_CHAR) != NO_TEXT) {
2177          A68G_SP = arg_sp;
2178          // INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
2179          PUSH_VALUE (p, VALUE (&width), A68G_INT);
2180          PUSH_VALUE (p, VALUE (&after) != 0 ? VALUE (&after) - 1 : 0, A68G_INT);
2181          PUSH_VALUE (p, VALUE (&expo) > 0 ? VALUE (&expo) + 1 : VALUE (&expo) - 1, A68G_INT);
2182          PUSH_VALUE (p, VALUE (&frmt), A68G_INT);
2183          return real (p);
2184        } else {
2185          A68G_SP = pop_sp;
2186          return s;
2187        }
2188      } else {
2189        char *s = stack_string (p, 8 + ABS (VALUE (&width)));
2190        A68G_SP = pop_sp;
2191        return error_chars (s, VALUE (&width));
2192      }
2193    }
2194    ABEND (A68G_TRUE, ERROR_INTERNAL_CONSISTENCY, __func__);
2195    return NO_TEXT;
2196  }
2197  
2198  //! @brief PROC (NUMBER, INT) STRING whole
2199  
2200  void genie_whole (NODE_T * p)
2201  {
2202    PUSH_REF(p, tmp_to_a68g_string (p, whole (p)));
2203  }
2204  
2205  //! @brief PROC (NUMBER, INT, INT) STRING bits 
2206  
2207  void genie_bits (NODE_T * p)
2208  {
2209    PUSH_REF (p, tmp_to_a68g_string (p, bits_to_string (p)));
2210  }
2211  
2212  //! @brief PROC (NUMBER, INT, INT) STRING fixed
2213  
2214  void genie_fixed (NODE_T * p)
2215  {
2216    PUSH_REF (p, tmp_to_a68g_string (p, fixed (p)));
2217  }
2218  
2219  //! @brief PROC (NUMBER, INT, INT, INT) STRING eng
2220  
2221  void genie_real (NODE_T * p)
2222  {
2223    PUSH_REF (p, tmp_to_a68g_string (p, real (p)));
2224  }
2225  
2226  //! @brief PROC (NUMBER, INT, INT, INT) STRING float
2227  
2228  void genie_float (NODE_T * p)
2229  {
2230    PUSH_VALUE (p, 1, A68G_INT);
2231    genie_real (p);
2232  }
2233  
2234  // ALGOL68C routines.
2235  
2236  //! @def A68C_TRANSPUT
2237  //! @brief Generate Algol68C routines readint, getint, etcetera.
2238  
2239  #define A68C_TRANSPUT(n, m)\
2240   void genie_get_##n (NODE_T * p)\
2241    {\
2242      A68G_REF ref_file;\
2243      POP_REF (p, &ref_file);\
2244      CHECK_REF (p, ref_file, M_REF_FILE);\
2245      BYTE_T *z = STACK_TOP;\
2246      INCREMENT_STACK_POINTER (p, SIZE (MODE (m)));\
2247      ADDR_T pop_sp = A68G_SP;\
2248      open_for_reading (p, ref_file);\
2249      genie_read_standard (p, MODE (m), z, ref_file);\
2250      A68G_SP = pop_sp;\
2251    }\
2252    void genie_put_##n (NODE_T * p)\
2253    {\
2254      size_t size = SIZE (MODE (m)), sizf = SIZE (M_REF_FILE);\
2255      A68G_REF ref_file = * (A68G_REF *) STACK_OFFSET (- (size + sizf));\
2256      CHECK_REF (p, ref_file, M_REF_FILE);\
2257      reset_transput_buffer (UNFORMATTED_BUFFER);\
2258      open_for_writing (p, ref_file);\
2259      genie_write_standard (p, MODE (m), STACK_OFFSET (-size), ref_file);\
2260      write_purge_buffer (p, ref_file, UNFORMATTED_BUFFER);\
2261      DECREMENT_STACK_POINTER (p, size + sizf);\
2262    }\
2263    void genie_read_##n (NODE_T * p)\
2264    {\
2265      BYTE_T *z = STACK_TOP;\
2266      INCREMENT_STACK_POINTER (p, SIZE (MODE (m)));\
2267      ADDR_T pop_sp = A68G_SP;\
2268      open_for_reading (p, A68G (stand_in));\
2269      genie_read_standard (p, MODE (m), z, A68G (stand_in));\
2270      A68G_SP = pop_sp;\
2271    }\
2272    void genie_print_##n (NODE_T * p)\
2273    {\
2274      size_t size = SIZE (MODE (m));\
2275      reset_transput_buffer (UNFORMATTED_BUFFER);\
2276      open_for_writing (p, A68G (stand_out));\
2277      genie_write_standard (p, MODE (m), STACK_OFFSET (-size), A68G (stand_out));\
2278      write_purge_buffer (p, A68G (stand_out), UNFORMATTED_BUFFER);\
2279      DECREMENT_STACK_POINTER (p, size);\
2280    }
2281  
2282  A68C_TRANSPUT (int, INT);
2283  A68C_TRANSPUT (long_int, LONG_INT);
2284  A68C_TRANSPUT (long_mp_int, LONG_LONG_INT);
2285  A68C_TRANSPUT (real, REAL);
2286  A68C_TRANSPUT (long_real, LONG_REAL);
2287  A68C_TRANSPUT (long_mp_real, LONG_LONG_REAL);
2288  A68C_TRANSPUT (bits, BITS);
2289  A68C_TRANSPUT (long_bits, LONG_BITS);
2290  A68C_TRANSPUT (long_mp_bits, LONG_LONG_BITS);
2291  A68C_TRANSPUT (bool, BOOL);
2292  A68C_TRANSPUT (char, CHAR);
2293  A68C_TRANSPUT (string, STRING);
2294  
2295  #undef A68C_TRANSPUT
2296  
2297  #define A68C_TRANSPUT(n, s, m)\
2298   void genie_get_##n (NODE_T * p) {\
2299      A68G_REF ref_file;\
2300      POP_REF (p, &ref_file);\
2301      CHECK_REF (p, ref_file, M_REF_FILE);\
2302      PUSH_REF (p, ref_file);\
2303      genie_get_##s (p);\
2304      PUSH_REF (p, ref_file);\
2305      genie_get_##s (p);\
2306    }\
2307    void genie_put_##n (NODE_T * p) {\
2308      size_t size = SIZE (MODE (m)), sizf = SIZE (M_REF_FILE);\
2309      A68G_REF ref_file = * (A68G_REF *) STACK_OFFSET (- (size + sizf));\
2310      CHECK_REF (p, ref_file, M_REF_FILE);\
2311      reset_transput_buffer (UNFORMATTED_BUFFER);\
2312      open_for_writing (p, ref_file);\
2313      genie_write_standard (p, MODE (m), STACK_OFFSET (-size), ref_file);\
2314      write_purge_buffer (p, ref_file, UNFORMATTED_BUFFER);\
2315      DECREMENT_STACK_POINTER (p, size + sizf);\
2316    }\
2317    void genie_read_##n (NODE_T * p) {\
2318      genie_read_##s (p);\
2319      genie_read_##s (p);\
2320    }\
2321    void genie_print_##n (NODE_T * p) {\
2322      size_t size = SIZE (MODE (m));\
2323      reset_transput_buffer (UNFORMATTED_BUFFER);\
2324      open_for_writing (p, A68G (stand_out));\
2325      genie_write_standard (p, MODE (m), STACK_OFFSET (-size), A68G (stand_out));\
2326      write_purge_buffer (p, A68G (stand_out), UNFORMATTED_BUFFER);\
2327      DECREMENT_STACK_POINTER (p, size);\
2328    }
2329  
2330  A68C_TRANSPUT (complex, real, COMPLEX);
2331  A68C_TRANSPUT (mp_complex, long_real, LONG_COMPLEX);
2332  A68C_TRANSPUT (long_mp_complex, long_mp_real, LONG_LONG_COMPLEX);
2333  
2334  #undef A68C_TRANSPUT
2335  
2336  //! @brief PROC STRING read line
2337  
2338  void genie_read_line (NODE_T * p)
2339  {
2340  #if defined (HAVE_READLINE)
2341    char *line = readline ("");
2342    if (line != NO_TEXT && strlen (line) > 0) {
2343      add_history (line);
2344    }
2345    PUSH_REF (p, c_to_a_string (p, line, DEFAULT_WIDTH));
2346    a68g_free (line);
2347  #else
2348    genie_read_string (p);
2349    genie_stand_in (p);
2350    genie_new_line (p);
2351  #endif
2352  }
     


© 2002-2025 J.M. van der Veer (jmvdveer@xs4all.nl)