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-2024 J. Marcel van der Veer [algol68g@xs4all.nl].
   8  
   9  //! @section License
  10  //!
  11  //! This program is free software; you can redistribute it and/or modify it 
  12  //! under the terms of the GNU General Public License as published by the 
  13  //! Free Software Foundation; either version 3 of the License, or 
  14  //! (at your option) any later version.
  15  //!
  16  //! This program is distributed in the hope that it will be useful, but 
  17  //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  18  //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
  19  //! more details. You should have received a copy of the GNU General Public 
  20  //! License along with this program. If not, see [http://www.gnu.org/licenses/].
  21  
  22  //! @section Synopsis 
  23  //!
  24  //! 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, A68_REF ref_file)
  36  {
  37    A68_FILE *f = FILE_DEREF (&ref_file);
  38    while ((*ch) != EOF_CHAR && IS_NL_FF (*ch)) {
  39      A68_BOOL *z = (A68_BOOL *) STACK_TOP;
  40      ADDR_T pop_sp = A68_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        A68_SP = pop_sp;
  45        if (VALUE (z) == A68_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        A68_SP = pop_sp;
  52        if (VALUE (z) == A68_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, A68_REF ref_file)
  64  {
  65    A68_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, A68_REF ref_file)
  91  {
  92    A68_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, A68_REF ref_file)
 148  {
 149    A68_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, A68_REF ref_file)
 171  {
 172    A68_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, A68_REF ref_file)
 184  {
 185    A68_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 = A68_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 = A68_FALSE;
 199        } else if (IS_NL_FF (ch)) {
 200          ADDR_T pop_sp = A68_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          A68_SP = pop_sp;
 208          siga = A68_FALSE;
 209        } else if (term != NO_TEXT && strchr (term, ch) != NO_TEXT) {
 210          siga = A68_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 a68_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    int len = (int) strlen (letters);
 230    BOOL_T good_file = A68_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/a68_", "./a68_", NO_TEXT };
 234    for (int i = 0; prefix[i] != NO_TEXT; i++) {
 235      for (int k = 0; k < TRIALS && good_file == A68_FALSE; k++) {
 236        a68_bufcpy (tfilename, prefix[i], BUFFER_SIZE);
 237        for (int j = 0; j < TMP_SIZE; j++) {
 238          int cindex;
 239          do {
 240            cindex = (int) (a68_unif_rand () * len);
 241          } while (cindex < 0 || cindex >= len);
 242          char chars[2];
 243          chars[0] = letters[cindex];
 244          chars[1] = NULL_CHAR;
 245          a68_bufcat (tfilename, chars, BUFFER_SIZE);
 246        }
 247        a68_bufcat (tfilename, ".tmp", BUFFER_SIZE);
 248        errno = 0;
 249        FILE_T fd = open (tfilename, flags | O_EXCL, permissions);
 250        good_file = (BOOL_T) (fd != A68_NO_FILE && errno == 0);
 251        if (good_file) {
 252          (void) close (fd);
 253        }
 254      }
 255    }
 256    if (good_file) {
 257      a68_bufcpy (fn, tfilename, BUFFER_SIZE);
 258      return A68_TRUE;
 259    } else {
 260      return A68_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, A68_REF ref_file, int flags, mode_t permissions)
 269  {
 270    BOOL_T reading = (flags & ~O_BINARY) == A68_READ_ACCESS;
 271    BOOL_T writing = (flags & ~O_BINARY) == A68_WRITE_ACCESS;
 272    ABEND (reading == writing, ERROR_INTERNAL_CONSISTENCY, __func__);
 273    CHECK_REF (p, ref_file, M_REF_FILE);
 274    A68_FILE *file = FILE_DEREF (&ref_file);
 275    CHECK_INIT (p, INITIALISED (file), M_FILE);
 276    if (!IS_NIL (STRING (file))) {
 277      if (writing) {
 278        A68_REF z = *DEREF (A68_REF, &STRING (file));
 279        A68_ARRAY *arr; A68_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) = A68_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 A68_NO_FILE;
 293      } else {
 294        BUFFER tfilename;
 295        int len;
 296        BUFCLR (tfilename);
 297        if (!a68_mkstemp (tfilename, flags, permissions)) {
 298          diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_NO_TEMP);
 299          exit_genie (p, A68_RUNTIME_ERROR);
 300        }
 301        FD (file) = open (tfilename, flags, permissions);
 302        len = 1 + (int) strlen (tfilename);
 303        IDENTIFICATION (file) = heap_generator (p, M_C_STRING, len);
 304        BLOCK_GC_HANDLE (&(IDENTIFICATION (file)));
 305        a68_bufcpy (DEREF (char, &IDENTIFICATION (file)), tfilename, len);
 306        TRANSPUT_BUFFER (file) = get_unblocked_transput_buffer (p);
 307        reset_transput_buffer (TRANSPUT_BUFFER (file));
 308        END_OF_FILE (file) = A68_FALSE;
 309        TMP_FILE (file) = A68_TRUE;
 310        FILE_ENTRY (file) = store_file_entry (p, FD (file), tfilename, TMP_FILE (file));
 311        return FD (file);
 312      }
 313    } else {
 314  // Opening an identified file.
 315      A68_REF ref_filename = IDENTIFICATION (file);
 316      CHECK_REF (p, ref_filename, M_ROWS);
 317      char *filename = DEREF (char, &ref_filename);
 318      if (OPEN_EXCLUSIVE (file)) {
 319  // Establishing requires that the file does not exist.
 320        if (flags == (A68_WRITE_ACCESS)) {
 321          flags |= O_EXCL;
 322        }
 323        OPEN_EXCLUSIVE (file) = A68_FALSE;
 324      }
 325      FD (file) = open (filename, flags, permissions);
 326      TRANSPUT_BUFFER (file) = get_unblocked_transput_buffer (p);
 327      reset_transput_buffer (TRANSPUT_BUFFER (file));
 328      END_OF_FILE (file) = A68_FALSE;
 329      FILE_ENTRY (file) = store_file_entry (p, FD (file), filename, TMP_FILE (file));
 330      return FD (file);
 331    }
 332  }
 333  
 334  //! @brief Call PROC (REF FILE) VOID during transput.
 335  
 336  void genie_call_proc_ref_file_void (NODE_T * p, A68_REF ref_file, A68_PROCEDURE z)
 337  {
 338    ADDR_T pop_sp = A68_SP, pop_fp = A68_FP;
 339    MOID_T *u = M_PROC_REF_FILE_VOID;
 340    PUSH_REF (p, ref_file);
 341    genie_call_procedure (p, MOID (&z), u, u, &z, pop_sp, pop_fp);
 342    A68_SP = pop_sp;              // Voiding
 343  }
 344  
 345  // Unformatted transput.
 346  
 347  //! @brief Hexadecimal value of digit.
 348  
 349  int char_value (int ch)
 350  {
 351    switch (ch) {
 352    case '0': {
 353        return 0;
 354      }
 355    case '1': {
 356        return 1;
 357      }
 358    case '2': {
 359        return 2;
 360      }
 361    case '3': {
 362        return 3;
 363      }
 364    case '4': {
 365        return 4;
 366      }
 367    case '5': {
 368        return 5;
 369      }
 370    case '6': {
 371        return 6;
 372      }
 373    case '7': {
 374        return 7;
 375      }
 376    case '8': {
 377        return 8;
 378      }
 379    case '9': {
 380        return 9;
 381      }
 382    case 'A':
 383    case 'a': {
 384        return 10;
 385      }
 386    case 'B':
 387    case 'b': {
 388        return 11;
 389      }
 390    case 'C':
 391    case 'c': {
 392        return 12;
 393      }
 394    case 'D':
 395    case 'd': {
 396        return 13;
 397      }
 398    case 'E':
 399    case 'e': {
 400        return 14;
 401      }
 402    case 'F':
 403    case 'f': {
 404        return 15;
 405      }
 406    default: {
 407        return -1;
 408      }
 409    }
 410  }
 411  
 412  //! @brief INT value of BITS denotation
 413  
 414  UNSIGNED_T bits_to_int (NODE_T * p, char *str)
 415  {
 416    errno = 0;
 417    char *radix = NO_TEXT, *end = NO_TEXT;
 418    int base = (int) a68_strtou (str, &radix, 10);
 419    if (radix != NO_TEXT && TO_UPPER (radix[0]) == TO_UPPER (RADIX_CHAR) && errno == 0) {
 420      UNSIGNED_T bits = 0;
 421      if (base < 2 || base > 16) {
 422        diagnostic (A68_RUNTIME_ERROR, p, ERROR_INVALID_RADIX, base);
 423        exit_genie (p, A68_RUNTIME_ERROR);
 424      }
 425      bits = a68_strtou (&(radix[1]), &end, base);
 426      if (end != NO_TEXT && end[0] == NULL_CHAR && errno == 0) {
 427        return bits;
 428      }
 429    }
 430    diagnostic (A68_RUNTIME_ERROR, p, ERROR_IN_DENOTATION, M_BITS);
 431    exit_genie (p, A68_RUNTIME_ERROR);
 432    return 0;
 433  }
 434  
 435  //! @brief Convert string to required mode and store.
 436  
 437  BOOL_T genie_string_to_value_internal (NODE_T * p, MOID_T * m, char *a, BYTE_T * item)
 438  {
 439    errno = 0;
 440  // strto.. does not mind empty strings.
 441    if (strlen (a) == 0) {
 442      return A68_FALSE;
 443    }
 444    if (m == M_INT) {
 445      A68_INT *z = (A68_INT *) item;
 446      char *end;
 447      VALUE (z) = (INT_T) a68_strtoi (a, &end, 10);
 448      if (end[0] == NULL_CHAR && errno == 0) {
 449        STATUS (z) = INIT_MASK;
 450        return A68_TRUE;
 451      } else {
 452        return A68_FALSE;
 453      }
 454    }
 455    if (m == M_REAL) {
 456      A68_REAL *z = (A68_REAL *) item;
 457      char *end;
 458      VALUE (z) = strtod (a, &end);
 459      if (end[0] == NULL_CHAR && errno == 0) {
 460        STATUS (z) = INIT_MASK;
 461        return A68_TRUE;
 462      } else {
 463        return A68_FALSE;
 464      }
 465    }
 466  #if (A68_LEVEL >= 3)
 467    if (m == M_LONG_INT) {
 468      A68_LONG_INT *z = (A68_LONG_INT *) item;
 469      if (string_to_double_int (p, z, a) == A68_FALSE) {
 470        return A68_FALSE;
 471      }
 472      STATUS (z) = INIT_MASK;
 473      return A68_TRUE;
 474    }
 475    if (m == M_LONG_REAL) {
 476      A68_LONG_REAL *z = (A68_LONG_REAL *) item;
 477      char *end;
 478  //  VALUE (z).f = strtoflt128 (a, &end);
 479      VALUE (z).f = string_to_double (a, &end);
 480      MATH_RTE (p, errno != 0, M_LONG_REAL, ERROR_MATH);
 481      if (end[0] == NULL_CHAR && errno == 0) {
 482        STATUS (z) = INIT_MASK;
 483        return A68_TRUE;
 484      } else {
 485        return A68_FALSE;
 486      }
 487    }
 488    if (m == M_LONG_BITS) {
 489      A68_LONG_BITS *z = (A68_LONG_BITS *) item;
 490      int ret = A68_TRUE;
 491      DOUBLE_NUM_T b;
 492      set_lw (b, 0x0);
 493      if (a[0] == FLIP_CHAR || a[0] == FLOP_CHAR) {
 494  // [] BOOL denotation is "TTFFFFTFT ...".
 495        if (strlen (a) > (size_t) A68_LONG_BITS_WIDTH) {
 496          errno = ERANGE;
 497          ret = A68_FALSE;
 498        } else {
 499          int n = 1;
 500          UNSIGNED_T k = 0x1;
 501          for (int j = (int) strlen (a) - 1; j >= 0; j--) {
 502            if (a[j] == FLIP_CHAR) {
 503              if (n <= A68_LONG_BITS_WIDTH / 2) {
 504                LW (b) |= k;
 505              } else {
 506                HW (b) |= k;
 507              }
 508            } else if (a[j] != FLOP_CHAR) {
 509              ret = A68_FALSE;
 510            }
 511            k <<= 1;
 512          }
 513        }
 514        VALUE (z) = b;
 515      } else {
 516  // BITS denotation.
 517        VALUE (z) = double_strtou (p, a);
 518      }
 519      return ret;
 520    }
 521  #else
 522    if (m == M_LONG_BITS || m == M_LONG_LONG_BITS) {
 523      int digits = DIGITS (m);
 524      int status = A68_TRUE;
 525      ADDR_T pop_sp = A68_SP;
 526      MP_T *z = (MP_T *) item;
 527      if (a[0] == FLIP_CHAR || a[0] == FLOP_CHAR) {
 528  // [] BOOL denotation is "TTFFFFTFT ...".
 529        if (strlen (a) > (size_t) A68_BITS_WIDTH) {
 530          errno = ERANGE;
 531          status = A68_FALSE;
 532        } else {
 533          MP_T *w = lit_mp (p, 1, 0, digits);
 534          SET_MP_ZERO (z, digits);
 535          for (int j = (int) strlen (a) - 1; j >= 0; j--) {
 536            if (a[j] == FLIP_CHAR) {
 537              (void) add_mp (p, z, z, w, digits);
 538            } else if (a[j] != FLOP_CHAR) {
 539              status = A68_FALSE;
 540            }
 541            (void) mul_mp_digit (p, w, w, (MP_T) 2, digits);
 542          }
 543        }
 544      } else {
 545  // BITS denotation is also allowed.
 546        mp_strtou (p, z, a, m);
 547      }
 548      A68_SP = pop_sp;
 549      if (errno != 0 || status == A68_FALSE) {
 550        return A68_FALSE;
 551      }
 552      MP_STATUS (z) = (MP_T) INIT_MASK;
 553      return A68_TRUE;
 554    }
 555  #endif
 556    if (m == M_LONG_INT || m == M_LONG_LONG_INT) {
 557      int digits = DIGITS (m);
 558      MP_T *z = (MP_T *) item;
 559      if (strtomp (p, z, a, digits) == NaN_MP) {
 560        return A68_FALSE;
 561      }
 562      if (!check_mp_int (z, m)) {
 563        errno = ERANGE;
 564        return A68_FALSE;
 565      }
 566      MP_STATUS (z) = (MP_T) INIT_MASK;
 567      return A68_TRUE;
 568    }
 569    if (m == M_LONG_REAL || m == M_LONG_LONG_REAL) {
 570      int digits = DIGITS (m);
 571      MP_T *z = (MP_T *) item;
 572      if (strtomp (p, z, a, digits) == NaN_MP) {
 573        return A68_FALSE;
 574      }
 575      MP_STATUS (z) = (MP_T) INIT_MASK;
 576      return A68_TRUE;
 577    }
 578    if (m == M_BOOL) {
 579      A68_BOOL *z = (A68_BOOL *) item;
 580      char q = a[0], flip = FLIP_CHAR, flop = FLOP_CHAR;
 581      if (q == flip || q == flop) {
 582        VALUE (z) = (BOOL_T) (q == flip);
 583        STATUS (z) = INIT_MASK;
 584        return A68_TRUE;
 585      } else {
 586        return A68_FALSE;
 587      }
 588    }
 589    if (m == M_BITS) {
 590      A68_BITS *z = (A68_BITS *) item;
 591      int status = A68_TRUE;
 592      if (a[0] == FLIP_CHAR || a[0] == FLOP_CHAR) {
 593  // [] BOOL denotation is "TTFFFFTFT ...".
 594        if (strlen (a) > (size_t) A68_BITS_WIDTH) {
 595          errno = ERANGE;
 596          status = A68_FALSE;
 597        } else {
 598          UNSIGNED_T k = 0x1;
 599          VALUE (z) = 0;
 600          for (int j = (int) strlen (a) - 1; j >= 0; j--) {
 601            if (a[j] == FLIP_CHAR) {
 602              VALUE (z) += k;
 603            } else if (a[j] != FLOP_CHAR) {
 604              status = A68_FALSE;
 605            }
 606            k <<= 1;
 607          }
 608        }
 609      } else {
 610  // BITS denotation is also allowed.
 611        VALUE (z) = bits_to_int (p, a);
 612      }
 613      if (errno != 0 || status == A68_FALSE) {
 614        return A68_FALSE;
 615      }
 616      STATUS (z) = INIT_MASK;
 617      return A68_TRUE;
 618    }
 619    return A68_FALSE;
 620  }
 621  
 622  //! @brief Convert string in input buffer to value of required mode.
 623  
 624  void genie_string_to_value (NODE_T * p, MOID_T * mode, BYTE_T * item, A68_REF ref_file)
 625  {
 626    char *str = get_transput_buffer (INPUT_BUFFER);
 627    errno = 0;
 628  // end string, just in case.
 629    plusab_transput_buffer (p, INPUT_BUFFER, NULL_CHAR);
 630    if (mode == M_INT) {
 631      if (genie_string_to_value_internal (p, mode, str, item) == A68_FALSE) {
 632        value_error (p, mode, ref_file);
 633      }
 634    } else if (mode == M_LONG_INT || mode == M_LONG_LONG_INT) {
 635      if (genie_string_to_value_internal (p, mode, str, item) == A68_FALSE) {
 636        value_error (p, mode, ref_file);
 637      }
 638    } else if (mode == M_REAL) {
 639      if (genie_string_to_value_internal (p, mode, str, item) == A68_FALSE) {
 640        value_error (p, mode, ref_file);
 641      }
 642    } else if (mode == M_LONG_REAL || mode == M_LONG_LONG_REAL) {
 643      if (genie_string_to_value_internal (p, mode, str, item) == A68_FALSE) {
 644        value_error (p, mode, ref_file);
 645      }
 646    } else if (mode == M_BOOL) {
 647      if (genie_string_to_value_internal (p, mode, str, item) == A68_FALSE) {
 648        value_error (p, mode, ref_file);
 649      }
 650    } else if (mode == M_BITS) {
 651      if (genie_string_to_value_internal (p, mode, str, item) == A68_FALSE) {
 652        value_error (p, mode, ref_file);
 653      }
 654    } else if (mode == M_LONG_BITS || mode == M_LONG_LONG_BITS) {
 655      if (genie_string_to_value_internal (p, mode, str, item) == A68_FALSE) {
 656        value_error (p, mode, ref_file);
 657      }
 658    } else if (mode == M_CHAR) {
 659      A68_CHAR *z = (A68_CHAR *) item;
 660      if (str[0] == NULL_CHAR) {
 661  //      value_error (p, mode, ref_file);.
 662        VALUE (z) = NULL_CHAR;
 663        STATUS (z) = INIT_MASK;
 664      } else {
 665        int len = (int) strlen (str);
 666        if (len == 0 || len > 1) {
 667          value_error (p, mode, ref_file);
 668        }
 669        VALUE (z) = str[0];
 670        STATUS (z) = INIT_MASK;
 671      }
 672    } else if (mode == M_STRING) {
 673      A68_REF z;
 674      z = c_to_a_string (p, str, get_transput_buffer_index (INPUT_BUFFER) - 1);
 675      *(A68_REF *) item = z;
 676    }
 677    if (errno != 0) {
 678      transput_error (p, ref_file, mode);
 679    }
 680  }
 681  
 682  //! @brief Read object from file.
 683  
 684  void genie_read_standard (NODE_T * p, MOID_T * mode, BYTE_T * item, A68_REF ref_file)
 685  {
 686    A68_FILE *f = FILE_DEREF (&ref_file);
 687    errno = 0;
 688    if (END_OF_FILE (f)) {
 689      end_of_file_error (p, ref_file);
 690    }
 691    if (mode == M_PROC_REF_FILE_VOID) {
 692      genie_call_proc_ref_file_void (p, ref_file, *(A68_PROCEDURE *) item);
 693    } else if (mode == M_FORMAT) {
 694      diagnostic (A68_RUNTIME_ERROR, p, ERROR_UNDEFINED_TRANSPUT, M_FORMAT);
 695      exit_genie (p, A68_RUNTIME_ERROR);
 696    } else if (mode == M_REF_SOUND) {
 697      read_sound (p, ref_file, DEREF (A68_SOUND, (A68_REF *) item));
 698    } else if (IS_REF (mode)) {
 699      CHECK_REF (p, *(A68_REF *) item, mode);
 700      genie_read_standard (p, SUB (mode), ADDRESS ((A68_REF *) item), ref_file);
 701    } else if (mode == M_INT || mode == M_LONG_INT || mode == M_LONG_LONG_INT) {
 702      scan_integer (p, ref_file);
 703      genie_string_to_value (p, mode, item, ref_file);
 704    } else if (mode == M_REAL || mode == M_LONG_REAL || mode == M_LONG_LONG_REAL) {
 705      scan_real (p, ref_file);
 706      genie_string_to_value (p, mode, item, ref_file);
 707    } else if (mode == M_BOOL) {
 708      scan_char (p, ref_file);
 709      genie_string_to_value (p, mode, item, ref_file);
 710    } else if (mode == M_CHAR) {
 711      scan_char (p, ref_file);
 712      genie_string_to_value (p, mode, item, ref_file);
 713    } else if (mode == M_BITS || mode == M_LONG_BITS || mode == M_LONG_LONG_BITS) {
 714      scan_bits (p, ref_file);
 715      genie_string_to_value (p, mode, item, ref_file);
 716    } else if (mode == M_STRING) {
 717      char *term = DEREF (char, &TERMINATOR (f));
 718      scan_string (p, term, ref_file);
 719      genie_string_to_value (p, mode, item, ref_file);
 720    } else if (IS_STRUCT (mode)) {
 721      for (PACK_T *q = PACK (mode); q != NO_PACK; FORWARD (q)) {
 722        genie_read_standard (p, MOID (q), &item[OFFSET (q)], ref_file);
 723      }
 724    } else if (IS_UNION (mode)) {
 725      A68_UNION *z = (A68_UNION *) item;
 726      if (!(STATUS (z) | INIT_MASK) || VALUE (z) == NULL) {
 727        diagnostic (A68_RUNTIME_ERROR, p, ERROR_EMPTY_VALUE, mode);
 728        exit_genie (p, A68_RUNTIME_ERROR);
 729      }
 730      genie_read_standard (p, (MOID_T *) (VALUE (z)), &item[A68_UNION_SIZE], ref_file);
 731    } else if (IS_ROW (mode) || IS_FLEX (mode)) {
 732      MOID_T *deflexed = DEFLEX (mode);
 733      A68_ARRAY *arr;
 734      A68_TUPLE *tup;
 735      CHECK_INIT (p, INITIALISED ((A68_REF *) item), mode);
 736      GET_DESCRIPTOR (arr, tup, (A68_REF *) item);
 737      if (get_row_size (tup, DIM (arr)) > 0) {
 738        BYTE_T *base_addr = DEREF (BYTE_T, &ARRAY (arr));
 739        BOOL_T done = A68_FALSE;
 740        initialise_internal_index (tup, DIM (arr));
 741        while (!done) {
 742          ADDR_T a68_index = calculate_internal_index (tup, DIM (arr));
 743          ADDR_T elem_addr = ROW_ELEMENT (arr, a68_index);
 744          genie_read_standard (p, SUB (deflexed), &base_addr[elem_addr], ref_file);
 745          done = increment_internal_index (tup, DIM (arr));
 746        }
 747      }
 748    }
 749    if (errno != 0) {
 750      transput_error (p, ref_file, mode);
 751    }
 752  }
 753  
 754  //! @brief PROC ([] SIMPLIN) VOID read
 755  
 756  void genie_read (NODE_T * p)
 757  {
 758    A68_REF row;
 759    POP_REF (p, &row);
 760    genie_stand_in (p);
 761    PUSH_REF (p, row);
 762    genie_read_file (p);
 763  }
 764  
 765  //! @brief Open for reading.
 766  
 767  void open_for_reading (NODE_T * p, A68_REF ref_file)
 768  {
 769    A68_FILE *file = FILE_DEREF (&ref_file);
 770    if (!OPENED (file)) {
 771      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
 772      exit_genie (p, A68_RUNTIME_ERROR);
 773    }
 774    if (DRAW_MOOD (file)) {
 775      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
 776      exit_genie (p, A68_RUNTIME_ERROR);
 777    }
 778    if (WRITE_MOOD (file)) {
 779      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "write");
 780      exit_genie (p, A68_RUNTIME_ERROR);
 781    }
 782    if (!GET (&CHANNEL (file))) {
 783      diagnostic (A68_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "getting");
 784      exit_genie (p, A68_RUNTIME_ERROR);
 785    }
 786    if (!READ_MOOD (file) && !WRITE_MOOD (file)) {
 787      if (IS_NIL (STRING (file))) {
 788        if ((FD (file) = open_physical_file (p, ref_file, A68_READ_ACCESS, 0)) == A68_NO_FILE) {
 789          open_error (p, ref_file, "getting");
 790        }
 791      } else {
 792        FD (file) = open_physical_file (p, ref_file, A68_READ_ACCESS, 0);
 793      }
 794      DRAW_MOOD (file) = A68_FALSE;
 795      READ_MOOD (file) = A68_TRUE;
 796      WRITE_MOOD (file) = A68_FALSE;
 797      CHAR_MOOD (file) = A68_TRUE;
 798    }
 799    if (!CHAR_MOOD (file)) {
 800      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "binary");
 801      exit_genie (p, A68_RUNTIME_ERROR);
 802    }
 803  }
 804  
 805  //! @brief PROC (REF FILE, [] SIMPLIN) VOID get
 806  
 807  void genie_read_file (NODE_T * p)
 808  {
 809    A68_REF row; A68_ARRAY *arr; A68_TUPLE *tup;
 810    POP_REF (p, &row);
 811    CHECK_REF (p, row, M_ROW_SIMPLIN);
 812    GET_DESCRIPTOR (arr, tup, &row);
 813    int elems = ROW_SIZE (tup);
 814    A68_REF ref_file;
 815    POP_REF (p, &ref_file);
 816    CHECK_REF (p, ref_file, M_REF_FILE);
 817    A68_FILE *file = FILE_DEREF (&ref_file);
 818    CHECK_INIT (p, INITIALISED (file), M_FILE);
 819    open_for_reading (p, ref_file);
 820  // Read.
 821    if (elems <= 0) {
 822      return;
 823    }
 824    BYTE_T *base_address = DEREF (BYTE_T, &ARRAY (arr));
 825    int elem_index = 0;
 826    for (int k = 0; k < elems; k++) {
 827      A68_UNION *z = (A68_UNION *) & base_address[elem_index];
 828      MOID_T *mode = (MOID_T *) (VALUE (z));
 829      BYTE_T *item = (BYTE_T *) & base_address[elem_index + A68_UNION_SIZE];
 830      genie_read_standard (p, mode, item, ref_file);
 831      elem_index += SIZE (M_SIMPLIN);
 832    }
 833  }
 834  
 835  //! @brief Convert value to string.
 836  
 837  void genie_value_to_string (NODE_T * p, MOID_T * moid, BYTE_T * item, int mod)
 838  {
 839    if (moid == M_INT) {
 840      A68_INT *z = (A68_INT *) item;
 841      PUSH_UNION (p, M_INT);
 842      PUSH_VALUE (p, VALUE (z), A68_INT);
 843      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_INT)));
 844      if (mod == FORMAT_ITEM_G) {
 845        PUSH_VALUE (p, A68_INT_WIDTH + 1, A68_INT);
 846        genie_whole (p);
 847      } else if (mod == FORMAT_ITEM_H) {
 848        PUSH_VALUE (p, A68_REAL_WIDTH + A68_EXP_WIDTH + 4, A68_INT);
 849        PUSH_VALUE (p, A68_REAL_WIDTH - 1, A68_INT);
 850        PUSH_VALUE (p, A68_EXP_WIDTH + 1, A68_INT);
 851        PUSH_VALUE (p, 3, A68_INT);
 852        genie_real (p);
 853      }
 854      return;
 855    }
 856  #if (A68_LEVEL >= 3)
 857    if (moid == M_LONG_INT) {
 858      A68_LONG_INT *z = (A68_LONG_INT *) item;
 859      PUSH_UNION (p, M_LONG_INT);
 860      PUSH (p, z, SIZE (M_LONG_INT));
 861      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_LONG_INT)));
 862      if (mod == FORMAT_ITEM_G) {
 863        PUSH_VALUE (p, A68_LONG_WIDTH + 1, A68_INT);
 864        genie_whole (p);
 865      } else if (mod == FORMAT_ITEM_H) {
 866        PUSH_VALUE (p, A68_LONG_REAL_WIDTH + A68_LONG_EXP_WIDTH + 4, A68_INT);
 867        PUSH_VALUE (p, A68_LONG_REAL_WIDTH - 1, A68_INT);
 868        PUSH_VALUE (p, A68_LONG_EXP_WIDTH + 1, A68_INT);
 869        PUSH_VALUE (p, 3, A68_INT);
 870        genie_real (p);
 871      }
 872      return;
 873    }
 874    if (moid == M_LONG_REAL) {
 875      A68_LONG_REAL *z = (A68_LONG_REAL *) item;
 876      PUSH_UNION (p, M_LONG_REAL);
 877      PUSH_VALUE (p, VALUE (z), A68_LONG_REAL);
 878      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_LONG_REAL)));
 879      PUSH_VALUE (p, A68_LONG_REAL_WIDTH + A68_LONG_EXP_WIDTH + 4, A68_INT);
 880      PUSH_VALUE (p, A68_LONG_REAL_WIDTH - 1, A68_INT);
 881      PUSH_VALUE (p, A68_LONG_EXP_WIDTH + 1, A68_INT);
 882      if (mod == FORMAT_ITEM_G) {
 883        genie_float (p);
 884      } else if (mod == FORMAT_ITEM_H) {
 885        PUSH_VALUE (p, 3, A68_INT);
 886        genie_real (p);
 887      }
 888      return;
 889    }
 890    if (moid == M_LONG_BITS) {
 891      A68_LONG_BITS *z = (A68_LONG_BITS *) item;
 892      char *s = stack_string (p, 8 + A68_LONG_BITS_WIDTH);
 893      int n = 0;
 894      for (int w = 0; w <= 1; w++) {
 895        UNSIGNED_T bit = D_SIGN;
 896        for (int j = 0; j < A68_BITS_WIDTH; j++) {
 897          if (w == 0) {
 898            s[n] = (char) ((HW (VALUE (z)) & bit) ? FLIP_CHAR : FLOP_CHAR);
 899          } else {
 900            s[n] = (char) ((LW (VALUE (z)) & bit) ? FLIP_CHAR : FLOP_CHAR);
 901          }
 902          bit >>= 1;
 903          n++;
 904        }
 905      }
 906      s[n] = NULL_CHAR;
 907      return;
 908    }
 909  #else
 910    if (moid == M_LONG_BITS || moid == M_LONG_LONG_BITS) {
 911      int bits = get_mp_bits_width (moid), word = get_mp_bits_words (moid);
 912      int pos = bits;
 913      char *str = stack_string (p, 8 + bits);
 914      ADDR_T pop_sp = A68_SP;
 915      unt *row = stack_mp_bits (p, (MP_T *) item, moid);
 916      str[pos--] = NULL_CHAR;
 917      while (pos >= 0) {
 918        unt bit = 0x1;
 919        for (int j = 0; j < MP_BITS_BITS && pos >= 0; j++) {
 920          str[pos--] = (char) ((row[word - 1] & bit) ? FLIP_CHAR : FLOP_CHAR);
 921          bit <<= 1;
 922        }
 923        word--;
 924      }
 925      A68_SP = pop_sp;
 926      return;
 927    }
 928  #endif
 929    if (moid == M_LONG_INT) {
 930      MP_T *z = (MP_T *) item;
 931      PUSH_UNION (p, M_LONG_INT);
 932      PUSH (p, z, SIZE (M_LONG_INT));
 933      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_LONG_INT)));
 934      if (mod == FORMAT_ITEM_G) {
 935        PUSH_VALUE (p, A68_LONG_WIDTH + 1, A68_INT);
 936        genie_whole (p);
 937      } else if (mod == FORMAT_ITEM_H) {
 938        PUSH_VALUE (p, A68_LONG_REAL_WIDTH + A68_LONG_EXP_WIDTH + 4, A68_INT);
 939        PUSH_VALUE (p, A68_LONG_REAL_WIDTH - 1, A68_INT);
 940        PUSH_VALUE (p, A68_LONG_EXP_WIDTH + 1, A68_INT);
 941        PUSH_VALUE (p, 3, A68_INT);
 942        genie_real (p);
 943      }
 944      return;
 945    }
 946    if (moid == M_LONG_LONG_INT) {
 947      MP_T *z = (MP_T *) item;
 948      PUSH_UNION (p, M_LONG_LONG_INT);
 949      PUSH (p, z, SIZE (M_LONG_LONG_INT));
 950      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_LONG_LONG_INT)));
 951      if (mod == FORMAT_ITEM_G) {
 952        PUSH_VALUE (p, A68_LONG_LONG_WIDTH + 1, A68_INT);
 953        genie_whole (p);
 954      } else if (mod == FORMAT_ITEM_H) {
 955        PUSH_VALUE (p, A68_LONG_LONG_REAL_WIDTH + A68_LONG_LONG_EXP_WIDTH + 4, A68_INT);
 956        PUSH_VALUE (p, A68_LONG_LONG_REAL_WIDTH - 1, A68_INT);
 957        PUSH_VALUE (p, A68_LONG_LONG_EXP_WIDTH + 1, A68_INT);
 958        PUSH_VALUE (p, 3, A68_INT);
 959        genie_real (p);
 960      }
 961      return;
 962    }
 963    if (moid == M_REAL) {
 964      A68_REAL *z = (A68_REAL *) item;
 965      PUSH_UNION (p, M_REAL);
 966      PUSH_VALUE (p, VALUE (z), A68_REAL);
 967      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_REAL)));
 968      PUSH_VALUE (p, A68_REAL_WIDTH + A68_EXP_WIDTH + 4, A68_INT);
 969      PUSH_VALUE (p, A68_REAL_WIDTH - 1, A68_INT);
 970      PUSH_VALUE (p, A68_EXP_WIDTH + 1, A68_INT);
 971      if (mod == FORMAT_ITEM_G) {
 972        genie_float (p);
 973      } else if (mod == FORMAT_ITEM_H) {
 974        PUSH_VALUE (p, 3, A68_INT);
 975        genie_real (p);
 976      }
 977      return;
 978    }
 979    if (moid == M_LONG_REAL) {
 980      MP_T *z = (MP_T *) item;
 981      PUSH_UNION (p, M_LONG_REAL);
 982      PUSH (p, z, (int) SIZE (M_LONG_REAL));
 983      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_LONG_REAL)));
 984      PUSH_VALUE (p, A68_LONG_REAL_WIDTH + A68_LONG_EXP_WIDTH + 4, A68_INT);
 985      PUSH_VALUE (p, A68_LONG_REAL_WIDTH - 1, A68_INT);
 986      PUSH_VALUE (p, A68_LONG_EXP_WIDTH + 1, A68_INT);
 987      if (mod == FORMAT_ITEM_G) {
 988        genie_float (p);
 989      } else if (mod == FORMAT_ITEM_H) {
 990        PUSH_VALUE (p, 3, A68_INT);
 991        genie_real (p);
 992      }
 993      return;
 994    }
 995    if (moid == M_LONG_LONG_REAL) {
 996      MP_T *z = (MP_T *) item;
 997      PUSH_UNION (p, M_LONG_LONG_REAL);
 998      PUSH (p, z, (int) SIZE (M_LONG_LONG_REAL));
 999      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_LONG_LONG_REAL)));
1000      PUSH_VALUE (p, A68_LONG_LONG_REAL_WIDTH + A68_LONG_LONG_EXP_WIDTH + 4, A68_INT);
1001      PUSH_VALUE (p, A68_LONG_LONG_REAL_WIDTH - 1, A68_INT);
1002      PUSH_VALUE (p, A68_LONG_LONG_EXP_WIDTH + 1, A68_INT);
1003      if (mod == FORMAT_ITEM_G) {
1004        genie_float (p);
1005      } else if (mod == FORMAT_ITEM_H) {
1006        PUSH_VALUE (p, 3, A68_INT);
1007        genie_real (p);
1008      }
1009      return;
1010    }
1011    if (moid == M_BITS) {
1012      A68_BITS *z = (A68_BITS *) item;
1013      char *str = stack_string (p, 8 + A68_BITS_WIDTH);
1014      UNSIGNED_T bit = 0x1;
1015      int j;
1016      for (j = 1; j < A68_BITS_WIDTH; j++) {
1017        bit <<= 1;
1018      }
1019      for (j = 0; j < A68_BITS_WIDTH; j++) {
1020        str[j] = (char) ((VALUE (z) & bit) ? FLIP_CHAR : FLOP_CHAR);
1021        bit >>= 1;
1022      }
1023      str[j] = NULL_CHAR;
1024      return;
1025    }
1026  }
1027  
1028  //! @brief Print object to file.
1029  
1030  void genie_write_standard (NODE_T * p, MOID_T * mode, BYTE_T * item, A68_REF ref_file)
1031  {
1032    errno = 0;
1033    ABEND (mode == NO_MOID, ERROR_INTERNAL_CONSISTENCY, __func__);
1034    if (mode == M_PROC_REF_FILE_VOID) {
1035      genie_call_proc_ref_file_void (p, ref_file, *(A68_PROCEDURE *) item);
1036    } else if (mode == M_FORMAT) {
1037      diagnostic (A68_RUNTIME_ERROR, p, ERROR_UNDEFINED_TRANSPUT, M_FORMAT);
1038      exit_genie (p, A68_RUNTIME_ERROR);
1039    } else if (mode == M_SOUND) {
1040      write_sound (p, ref_file, (A68_SOUND *) item);
1041    } else if (mode == M_INT || mode == M_LONG_INT || mode == M_LONG_LONG_INT) {
1042      genie_value_to_string (p, mode, item, FORMAT_ITEM_G);
1043      add_string_from_stack_transput_buffer (p, UNFORMATTED_BUFFER);
1044    } else if (mode == M_REAL || mode == M_LONG_REAL || mode == M_LONG_LONG_REAL) {
1045      genie_value_to_string (p, mode, item, FORMAT_ITEM_G);
1046      add_string_from_stack_transput_buffer (p, UNFORMATTED_BUFFER);
1047    } else if (mode == M_BOOL) {
1048      A68_BOOL *z = (A68_BOOL *) item;
1049      char flipflop = (char) (VALUE (z) == A68_TRUE ? FLIP_CHAR : FLOP_CHAR);
1050      plusab_transput_buffer (p, UNFORMATTED_BUFFER, flipflop);
1051    } else if (mode == M_CHAR) {
1052      A68_CHAR *ch = (A68_CHAR *) item;
1053      plusab_transput_buffer (p, UNFORMATTED_BUFFER, (char) VALUE (ch));
1054    } else if (mode == M_BITS || mode == M_LONG_BITS || mode == M_LONG_LONG_BITS) {
1055      char *str = (char *) STACK_TOP;
1056      genie_value_to_string (p, mode, item, FORMAT_ITEM_G);
1057      add_string_transput_buffer (p, UNFORMATTED_BUFFER, str);
1058    } else if (mode == M_ROW_CHAR || mode == M_STRING) {
1059  // Handle these separately since this is faster than straightening.
1060      add_a_string_transput_buffer (p, UNFORMATTED_BUFFER, item);
1061    } else if (IS_UNION (mode)) {
1062      A68_UNION *z = (A68_UNION *) item;
1063      MOID_T *um = (MOID_T *) (VALUE (z));
1064      BYTE_T *ui = &item[A68_UNION_SIZE];
1065      if (um == NO_MOID) {
1066        diagnostic (A68_RUNTIME_ERROR, p, ERROR_EMPTY_VALUE, mode);
1067        exit_genie (p, A68_RUNTIME_ERROR);
1068      }
1069      genie_write_standard (p, um, ui, ref_file);
1070    } else if (IS_STRUCT (mode)) {
1071      for (PACK_T *q = PACK (mode); q != NO_PACK; FORWARD (q)) {
1072        BYTE_T *elem = &item[OFFSET (q)];
1073        genie_check_initialisation (p, elem, MOID (q));
1074        genie_write_standard (p, MOID (q), elem, ref_file);
1075      }
1076    } else if (IS_ROW (mode) || IS_FLEX (mode)) {
1077      MOID_T *deflexed = DEFLEX (mode);
1078      A68_ARRAY *arr;
1079      A68_TUPLE *tup;
1080      CHECK_INIT (p, INITIALISED ((A68_REF *) item), M_ROWS);
1081      GET_DESCRIPTOR (arr, tup, (A68_REF *) item);
1082      if (get_row_size (tup, DIM (arr)) > 0) {
1083        BYTE_T *base_addr = DEREF (BYTE_T, &ARRAY (arr));
1084        BOOL_T done = A68_FALSE;
1085        initialise_internal_index (tup, DIM (arr));
1086        while (!done) {
1087          ADDR_T a68_index = calculate_internal_index (tup, DIM (arr));
1088          ADDR_T elem_addr = ROW_ELEMENT (arr, a68_index);
1089          BYTE_T *elem = &base_addr[elem_addr];
1090          genie_check_initialisation (p, elem, SUB (deflexed));
1091          genie_write_standard (p, SUB (deflexed), elem, ref_file);
1092          done = increment_internal_index (tup, DIM (arr));
1093        }
1094      }
1095    }
1096    if (errno != 0) {
1097      ABEND (IS_NIL (ref_file), ERROR_ACTION, error_specification ());
1098      transput_error (p, ref_file, mode);
1099    }
1100  }
1101  
1102  //! @brief PROC ([] SIMPLOUT) VOID print, write
1103  
1104  void genie_write (NODE_T * p)
1105  {
1106    A68_REF row;
1107    POP_REF (p, &row);
1108    genie_stand_out (p);
1109    PUSH_REF (p, row);
1110    genie_write_file (p);
1111  }
1112  
1113  //! @brief Open for writing.
1114  
1115  void open_for_writing (NODE_T * p, A68_REF ref_file)
1116  {
1117    A68_FILE *file = FILE_DEREF (&ref_file);
1118    if (!OPENED (file)) {
1119      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
1120      exit_genie (p, A68_RUNTIME_ERROR);
1121    }
1122    if (DRAW_MOOD (file)) {
1123      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
1124      exit_genie (p, A68_RUNTIME_ERROR);
1125    }
1126    if (READ_MOOD (file)) {
1127      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "read");
1128      exit_genie (p, A68_RUNTIME_ERROR);
1129    }
1130    if (!PUT (&CHANNEL (file))) {
1131      diagnostic (A68_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "putting");
1132      exit_genie (p, A68_RUNTIME_ERROR);
1133    }
1134    if (!READ_MOOD (file) && !WRITE_MOOD (file)) {
1135      if (IS_NIL (STRING (file))) {
1136        if ((FD (file) = open_physical_file (p, ref_file, A68_WRITE_ACCESS, A68_PROTECTION)) == A68_NO_FILE) {
1137          open_error (p, ref_file, "putting");
1138        }
1139      } else {
1140        FD (file) = open_physical_file (p, ref_file, A68_WRITE_ACCESS, 0);
1141      }
1142      DRAW_MOOD (file) = A68_FALSE;
1143      READ_MOOD (file) = A68_FALSE;
1144      WRITE_MOOD (file) = A68_TRUE;
1145      CHAR_MOOD (file) = A68_TRUE;
1146    }
1147    if (!CHAR_MOOD (file)) {
1148      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "binary");
1149      exit_genie (p, A68_RUNTIME_ERROR);
1150    }
1151  }
1152  
1153  //! @brief PROC (REF FILE, [] SIMPLOUT) VOID put
1154  
1155  void genie_write_file (NODE_T * p)
1156  {
1157    A68_REF row; A68_ARRAY *arr; A68_TUPLE *tup;
1158    POP_REF (p, &row);
1159    CHECK_REF (p, row, M_ROW_SIMPLOUT);
1160    GET_DESCRIPTOR (arr, tup, &row);
1161    int elems = ROW_SIZE (tup);
1162    A68_REF ref_file;
1163    POP_REF (p, &ref_file);
1164    CHECK_REF (p, ref_file, M_REF_FILE);
1165    A68_FILE *file = FILE_DEREF (&ref_file);
1166    CHECK_INIT (p, INITIALISED (file), M_FILE);
1167    open_for_writing (p, ref_file);
1168  // Write.
1169    if (elems <= 0) {
1170      return;
1171    }
1172    BYTE_T *base_address = DEREF (BYTE_T, &ARRAY (arr));
1173    int elem_index = 0;
1174    for (int k = 0; k < elems; k++) {
1175      A68_UNION *z = (A68_UNION *) & (base_address[elem_index]);
1176      MOID_T *mode = (MOID_T *) (VALUE (z));
1177      BYTE_T *item = (BYTE_T *) & base_address[elem_index + A68_UNION_SIZE];
1178      reset_transput_buffer (UNFORMATTED_BUFFER);
1179      genie_write_standard (p, mode, item, ref_file);
1180      write_purge_buffer (p, ref_file, UNFORMATTED_BUFFER);
1181      elem_index += SIZE (M_SIMPLOUT);
1182    }
1183  }
1184  
1185  //! @brief Read object binary from file.
1186  
1187  void genie_read_bin_standard (NODE_T * p, MOID_T * mode, BYTE_T * item, A68_REF ref_file)
1188  {
1189    CHECK_REF (p, ref_file, M_REF_FILE);
1190    A68_FILE *f = FILE_DEREF (&ref_file);
1191    errno = 0;
1192    if (END_OF_FILE (f)) {
1193      end_of_file_error (p, ref_file);
1194    }
1195    if (mode == M_PROC_REF_FILE_VOID) {
1196      genie_call_proc_ref_file_void (p, ref_file, *(A68_PROCEDURE *) item);
1197    } else if (mode == M_FORMAT) {
1198      diagnostic (A68_RUNTIME_ERROR, p, ERROR_UNDEFINED_TRANSPUT, M_FORMAT);
1199      exit_genie (p, A68_RUNTIME_ERROR);
1200    } else if (mode == M_REF_SOUND) {
1201      read_sound (p, ref_file, (A68_SOUND *) ADDRESS ((A68_REF *) item));
1202    } else if (IS_REF (mode)) {
1203      CHECK_REF (p, *(A68_REF *) item, mode);
1204      genie_read_bin_standard (p, SUB (mode), ADDRESS ((A68_REF *) item), ref_file);
1205    } else if (mode == M_INT) {
1206      A68_INT *z = (A68_INT *) item;
1207      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1208      STATUS (z) = INIT_MASK;
1209    } else if (mode == M_LONG_INT) {
1210  #if (A68_LEVEL >= 3)
1211      A68_LONG_INT *z = (A68_LONG_INT *) item;
1212      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1213      STATUS (z) = INIT_MASK;
1214  #else
1215      MP_T *z = (MP_T *) item;
1216      ASSERT (io_read (FD (f), z, (size_t) SIZE (mode)) != -1);
1217      MP_STATUS (z) = (MP_T) INIT_MASK;
1218  #endif
1219    } else if (mode == M_LONG_LONG_INT) {
1220      MP_T *z = (MP_T *) item;
1221      ASSERT (io_read (FD (f), z, (size_t) SIZE (mode)) != -1);
1222      MP_STATUS (z) = (MP_T) INIT_MASK;
1223    } else if (mode == M_REAL) {
1224      A68_REAL *z = (A68_REAL *) item;
1225      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1226      STATUS (z) = INIT_MASK;
1227    } else if (mode == M_LONG_REAL) {
1228  #if (A68_LEVEL >= 3)
1229      A68_LONG_REAL *z = (A68_LONG_REAL *) item;
1230      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1231      STATUS (z) = INIT_MASK;
1232  #else
1233      MP_T *z = (MP_T *) item;
1234      ASSERT (io_read (FD (f), z, (size_t) SIZE (mode)) != -1);
1235      MP_STATUS (z) = (MP_T) INIT_MASK;
1236  #endif
1237    } else if (mode == M_LONG_LONG_REAL) {
1238      MP_T *z = (MP_T *) item;
1239      ASSERT (io_read (FD (f), z, (size_t) SIZE (mode)) != -1);
1240      MP_STATUS (z) = (MP_T) INIT_MASK;
1241    } else if (mode == M_BOOL) {
1242      A68_BOOL *z = (A68_BOOL *) item;
1243      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1244      STATUS (z) = INIT_MASK;
1245    } else if (mode == M_CHAR) {
1246      A68_CHAR *z = (A68_CHAR *) item;
1247      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1248      STATUS (z) = INIT_MASK;
1249    } else if (mode == M_BITS) {
1250      A68_BITS *z = (A68_BITS *) item;
1251      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1252      STATUS (z) = INIT_MASK;
1253    } else if (mode == M_LONG_BITS) {
1254  #if (A68_LEVEL >= 3)
1255      A68_LONG_BITS *z = (A68_LONG_BITS *) item;
1256      ASSERT (io_read (FD (f), &(VALUE (z)), sizeof (VALUE (z))) != -1);
1257      STATUS (z) = INIT_MASK;
1258  #else
1259      MP_T *z = (MP_T *) item;
1260      ASSERT (io_read (FD (f), z, (size_t) SIZE (mode)) != -1);
1261      MP_STATUS (z) = (MP_T) INIT_MASK;
1262  #endif
1263    } else if (mode == M_LONG_LONG_BITS) {
1264      MP_T *z = (MP_T *) item;
1265      ASSERT (io_read (FD (f), z, (size_t) SIZE (mode)) != -1);
1266      MP_STATUS (z) = (MP_T) INIT_MASK;
1267    } else if (mode == M_ROW_CHAR || mode == M_STRING) {
1268      int len;
1269      ASSERT (io_read (FD (f), &(len), sizeof (len)) != -1);
1270      reset_transput_buffer (UNFORMATTED_BUFFER);
1271      for (int k = 0; k < len; k++) {
1272        char ch;
1273        ASSERT (io_read (FD (f), &(ch), sizeof (char)) != -1);
1274        plusab_transput_buffer (p, UNFORMATTED_BUFFER, ch);
1275      }
1276      *(A68_REF *) item = c_to_a_string (p, get_transput_buffer (UNFORMATTED_BUFFER), DEFAULT_WIDTH);
1277    } else if (IS_UNION (mode)) {
1278      A68_UNION *z = (A68_UNION *) item;
1279      if (!(STATUS (z) | INIT_MASK) || VALUE (z) == NULL) {
1280        diagnostic (A68_RUNTIME_ERROR, p, ERROR_EMPTY_VALUE, mode);
1281        exit_genie (p, A68_RUNTIME_ERROR);
1282      }
1283      genie_read_bin_standard (p, (MOID_T *) (VALUE (z)), &item[A68_UNION_SIZE], ref_file);
1284    } else if (IS_STRUCT (mode)) {
1285      for (PACK_T *q = PACK (mode); q != NO_PACK; FORWARD (q)) {
1286        genie_read_bin_standard (p, MOID (q), &item[OFFSET (q)], ref_file);
1287      }
1288    } else if (IS_ROW (mode) || IS_FLEX (mode)) {
1289      MOID_T *deflexed = DEFLEX (mode);
1290      A68_ARRAY *arr; A68_TUPLE *tup;
1291      CHECK_INIT (p, INITIALISED ((A68_REF *) item), M_ROWS);
1292      GET_DESCRIPTOR (arr, tup, (A68_REF *) item);
1293      if (get_row_size (tup, DIM (arr)) > 0) {
1294        BYTE_T *base_addr = DEREF (BYTE_T, &ARRAY (arr));
1295        BOOL_T done = A68_FALSE;
1296        initialise_internal_index (tup, DIM (arr));
1297        while (!done) {
1298          ADDR_T a68_index = calculate_internal_index (tup, DIM (arr));
1299          ADDR_T elem_addr = ROW_ELEMENT (arr, a68_index);
1300          genie_read_bin_standard (p, SUB (deflexed), &base_addr[elem_addr], ref_file);
1301          done = increment_internal_index (tup, DIM (arr));
1302        }
1303      }
1304    }
1305    if (errno != 0) {
1306      transput_error (p, ref_file, mode);
1307    }
1308  }
1309  
1310  //! @brief PROC ([] SIMPLIN) VOID read bin
1311  
1312  void genie_read_bin (NODE_T * p)
1313  {
1314    A68_REF row;
1315    POP_REF (p, &row);
1316    genie_stand_back (p);
1317    PUSH_REF (p, row);
1318    genie_read_bin_file (p);
1319  }
1320  
1321  //! @brief PROC (REF FILE, [] SIMPLIN) VOID get bin
1322  
1323  void genie_read_bin_file (NODE_T * p)
1324  {
1325    A68_REF row; A68_ARRAY *arr; A68_TUPLE *tup;
1326    POP_REF (p, &row);
1327    CHECK_REF (p, row, M_ROW_SIMPLIN);
1328    GET_DESCRIPTOR (arr, tup, &row);
1329    int elems = ROW_SIZE (tup);
1330    A68_REF ref_file;
1331    POP_REF (p, &ref_file);
1332    ref_file = *(A68_REF *) STACK_TOP;
1333    CHECK_REF (p, ref_file, M_REF_FILE);
1334    A68_FILE *file = FILE_DEREF (&ref_file);
1335    CHECK_INIT (p, INITIALISED (file), M_FILE);
1336    if (!OPENED (file)) {
1337      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
1338      exit_genie (p, A68_RUNTIME_ERROR);
1339    }
1340    if (DRAW_MOOD (file)) {
1341      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
1342      exit_genie (p, A68_RUNTIME_ERROR);
1343    }
1344    if (WRITE_MOOD (file)) {
1345      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "write");
1346      exit_genie (p, A68_RUNTIME_ERROR);
1347    }
1348    if (!GET (&CHANNEL (file))) {
1349      diagnostic (A68_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "getting");
1350      exit_genie (p, A68_RUNTIME_ERROR);
1351    }
1352    if (!BIN (&CHANNEL (file))) {
1353      diagnostic (A68_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "binary getting");
1354      exit_genie (p, A68_RUNTIME_ERROR);
1355    }
1356    if (!READ_MOOD (file) && !WRITE_MOOD (file)) {
1357      if ((FD (file) = open_physical_file (p, ref_file, A68_READ_ACCESS | O_BINARY, 0)) == A68_NO_FILE) {
1358        open_error (p, ref_file, "binary getting");
1359      }
1360      DRAW_MOOD (file) = A68_FALSE;
1361      READ_MOOD (file) = A68_TRUE;
1362      WRITE_MOOD (file) = A68_FALSE;
1363      CHAR_MOOD (file) = A68_FALSE;
1364    }
1365    if (CHAR_MOOD (file)) {
1366      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "text");
1367      exit_genie (p, A68_RUNTIME_ERROR);
1368    }
1369  // Read.
1370    if (elems <= 0) {
1371      return;
1372    }
1373    int elem_index = 0;
1374    BYTE_T *base_address = DEREF (BYTE_T, &ARRAY (arr));
1375    for (int k = 0; k < elems; k++) {
1376      A68_UNION *z = (A68_UNION *) & base_address[elem_index];
1377      MOID_T *mode = (MOID_T *) (VALUE (z));
1378      BYTE_T *item = (BYTE_T *) & base_address[elem_index + A68_UNION_SIZE];
1379      genie_read_bin_standard (p, mode, item, ref_file);
1380      elem_index += SIZE (M_SIMPLIN);
1381    }
1382  }
1383  
1384  //! @brief Write object binary to file.
1385  
1386  void genie_write_bin_standard (NODE_T * p, MOID_T * mode, BYTE_T * item, A68_REF ref_file)
1387  {
1388    CHECK_REF (p, ref_file, M_REF_FILE);
1389    A68_FILE *f = FILE_DEREF (&ref_file);
1390    errno = 0;
1391    if (mode == M_PROC_REF_FILE_VOID) {
1392      genie_call_proc_ref_file_void (p, ref_file, *(A68_PROCEDURE *) item);
1393    } else if (mode == M_FORMAT) {
1394      diagnostic (A68_RUNTIME_ERROR, p, ERROR_UNDEFINED_TRANSPUT, M_FORMAT);
1395      exit_genie (p, A68_RUNTIME_ERROR);
1396    } else if (mode == M_SOUND) {
1397      write_sound (p, ref_file, (A68_SOUND *) item);
1398    } else if (mode == M_INT) {
1399      ASSERT (io_write (FD (f), &(VALUE ((A68_INT *) item)), sizeof (VALUE ((A68_INT *) item))) != -1);
1400    } else if (mode == M_LONG_INT) {
1401  #if (A68_LEVEL >= 3)
1402      ASSERT (io_write (FD (f), &(VALUE ((A68_LONG_INT *) item)), sizeof (VALUE ((A68_LONG_INT *) item))) != -1);
1403  #else
1404      ASSERT (io_write (FD (f), (MP_T *) item, (size_t) SIZE (mode)) != -1);
1405  #endif
1406    } else if (mode == M_LONG_LONG_INT) {
1407      ASSERT (io_write (FD (f), (MP_T *) item, (size_t) SIZE (mode)) != -1);
1408    } else if (mode == M_REAL) {
1409      ASSERT (io_write (FD (f), &(VALUE ((A68_REAL *) item)), sizeof (VALUE ((A68_REAL *) item))) != -1);
1410    } else if (mode == M_LONG_REAL) {
1411  #if (A68_LEVEL >= 3)
1412      ASSERT (io_write (FD (f), &(VALUE ((A68_LONG_REAL *) item)), sizeof (VALUE ((A68_LONG_REAL *) item))) != -1);
1413  #else
1414      ASSERT (io_write (FD (f), (MP_T *) item, (size_t) SIZE (mode)) != -1);
1415  #endif
1416    } else if (mode == M_LONG_LONG_REAL) {
1417      ASSERT (io_write (FD (f), (MP_T *) item, (size_t) SIZE (mode)) != -1);
1418    } else if (mode == M_BOOL) {
1419      ASSERT (io_write (FD (f), &(VALUE ((A68_BOOL *) item)), sizeof (VALUE ((A68_BOOL *) item))) != -1);
1420    } else if (mode == M_CHAR) {
1421      ASSERT (io_write (FD (f), &(VALUE ((A68_CHAR *) item)), sizeof (VALUE ((A68_CHAR *) item))) != -1);
1422    } else if (mode == M_BITS) {
1423      ASSERT (io_write (FD (f), &(VALUE ((A68_BITS *) item)), sizeof (VALUE ((A68_BITS *) item))) != -1);
1424    } else if (mode == M_LONG_BITS) {
1425  #if (A68_LEVEL >= 3)
1426      ASSERT (io_write (FD (f), &(VALUE ((A68_LONG_BITS *) item)), sizeof (VALUE ((A68_LONG_BITS *) item))) != -1);
1427  #else
1428      ASSERT (io_write (FD (f), (MP_T *) item, (size_t) SIZE (mode)) != -1);
1429  #endif
1430    } else if (mode == M_LONG_LONG_BITS) {
1431      ASSERT (io_write (FD (f), (MP_T *) item, (size_t) SIZE (mode)) != -1);
1432    } else if (mode == M_ROW_CHAR || mode == M_STRING) {
1433      reset_transput_buffer (UNFORMATTED_BUFFER);
1434      add_a_string_transput_buffer (p, UNFORMATTED_BUFFER, item);
1435      int len = get_transput_buffer_index (UNFORMATTED_BUFFER);
1436      ASSERT (io_write (FD (f), &(len), sizeof (len)) != -1);
1437      WRITE (FD (f), get_transput_buffer (UNFORMATTED_BUFFER));
1438    } else if (IS_UNION (mode)) {
1439      A68_UNION *z = (A68_UNION *) item;
1440      genie_write_bin_standard (p, (MOID_T *) (VALUE (z)), &item[A68_UNION_SIZE], ref_file);
1441    } else if (IS_STRUCT (mode)) {
1442      for (PACK_T *q = PACK (mode); q != NO_PACK; FORWARD (q)) {
1443        BYTE_T *elem = &item[OFFSET (q)];
1444        genie_check_initialisation (p, elem, MOID (q));
1445        genie_write_bin_standard (p, MOID (q), elem, ref_file);
1446      }
1447    } else if (IS_ROW (mode) || IS_FLEX (mode)) {
1448      MOID_T *deflexed = DEFLEX (mode);
1449      A68_ARRAY *arr; A68_TUPLE *tup;
1450      CHECK_INIT (p, INITIALISED ((A68_REF *) item), M_ROWS);
1451      GET_DESCRIPTOR (arr, tup, (A68_REF *) item);
1452      if (get_row_size (tup, DIM (arr)) > 0) {
1453        BYTE_T *base_addr = DEREF (BYTE_T, &ARRAY (arr));
1454        BOOL_T done = A68_FALSE;
1455        initialise_internal_index (tup, DIM (arr));
1456        while (!done) {
1457          ADDR_T a68_index = calculate_internal_index (tup, DIM (arr));
1458          ADDR_T elem_addr = ROW_ELEMENT (arr, a68_index);
1459          BYTE_T *elem = &base_addr[elem_addr];
1460          genie_check_initialisation (p, elem, SUB (deflexed));
1461          genie_write_bin_standard (p, SUB (deflexed), elem, ref_file);
1462          done = increment_internal_index (tup, DIM (arr));
1463        }
1464      }
1465    }
1466    if (errno != 0) {
1467      transput_error (p, ref_file, mode);
1468    }
1469  }
1470  
1471  //! @brief PROC ([] SIMPLOUT) VOID write bin, print bin
1472  
1473  void genie_write_bin (NODE_T * p)
1474  {
1475    A68_REF row;
1476    POP_REF (p, &row);
1477    genie_stand_back (p);
1478    PUSH_REF (p, row);
1479    genie_write_bin_file (p);
1480  }
1481  
1482  //! @brief PROC (REF FILE, [] SIMPLOUT) VOID put bin
1483  
1484  void genie_write_bin_file (NODE_T * p)
1485  {
1486    A68_REF row; A68_ARRAY *arr; A68_TUPLE *tup;
1487    POP_REF (p, &row);
1488    CHECK_REF (p, row, M_ROW_SIMPLOUT);
1489    GET_DESCRIPTOR (arr, tup, &row);
1490    int elems = ROW_SIZE (tup);
1491    A68_REF ref_file;
1492    POP_REF (p, &ref_file);
1493    ref_file = *(A68_REF *) STACK_TOP;
1494    CHECK_REF (p, ref_file, M_REF_FILE);
1495    A68_FILE *file = FILE_DEREF (&ref_file);
1496    CHECK_INIT (p, INITIALISED (file), M_FILE);
1497    if (!OPENED (file)) {
1498      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
1499      exit_genie (p, A68_RUNTIME_ERROR);
1500    }
1501    if (DRAW_MOOD (file)) {
1502      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
1503      exit_genie (p, A68_RUNTIME_ERROR);
1504    }
1505    if (READ_MOOD (file)) {
1506      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "read");
1507      exit_genie (p, A68_RUNTIME_ERROR);
1508    }
1509    if (!PUT (&CHANNEL (file))) {
1510      diagnostic (A68_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "putting");
1511      exit_genie (p, A68_RUNTIME_ERROR);
1512    }
1513    if (!BIN (&CHANNEL (file))) {
1514      diagnostic (A68_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "binary putting");
1515      exit_genie (p, A68_RUNTIME_ERROR);
1516    }
1517    if (!READ_MOOD (file) && !WRITE_MOOD (file)) {
1518      if ((FD (file) = open_physical_file (p, ref_file, A68_WRITE_ACCESS | O_BINARY, A68_PROTECTION)) == A68_NO_FILE) {
1519        open_error (p, ref_file, "binary putting");
1520      }
1521      DRAW_MOOD (file) = A68_FALSE;
1522      READ_MOOD (file) = A68_FALSE;
1523      WRITE_MOOD (file) = A68_TRUE;
1524      CHAR_MOOD (file) = A68_FALSE;
1525    }
1526    if (CHAR_MOOD (file)) {
1527      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "text");
1528      exit_genie (p, A68_RUNTIME_ERROR);
1529    }
1530    if (elems <= 0) {
1531      return;
1532    }
1533    BYTE_T *base_address = DEREF (BYTE_T, &ARRAY (arr));
1534    int elem_index = 0;
1535    for (int k = 0; k < elems; k++) {
1536      A68_UNION *z = (A68_UNION *) & base_address[elem_index];
1537      MOID_T *mode = (MOID_T *) (VALUE (z));
1538      BYTE_T *item = (BYTE_T *) & base_address[elem_index + A68_UNION_SIZE];
1539      genie_write_bin_standard (p, mode, item, ref_file);
1540      elem_index += SIZE (M_SIMPLOUT);
1541    }
1542  }
1543  
1544  // Next are formatting routines "whole", "fixed" and "float" for mode
1545  // INT, LONG INT and LONG LONG INT, and REAL, LONG REAL and LONG LONG REAL.
1546  // They are direct implementations of the routines described in the
1547  // Revised Report, although those were only meant as a specification.
1548  // The rest of Algol68G should only reference "genie_whole", "genie_fixed"
1549  // or "genie_float" since internal routines like "sub_fixed" may leave the
1550  // stack corrupted when called directly.
1551  
1552  //! @brief Generate a string of error chars.
1553  
1554  char *error_chars (char *s, int n)
1555  {
1556    int k = (n != 0 ? ABS (n) : 1);
1557    s[k] = NULL_CHAR;
1558    while (--k >= 0) {
1559      s[k] = ERROR_CHAR;
1560    }
1561    return s;
1562  }
1563  
1564  //! @brief Convert temporary C string to A68 string.
1565  
1566  A68_REF tmp_to_a68_string (NODE_T * p, char *temp_string)
1567  {
1568  // no compaction allowed since temp_string might be up for garbage collecting ...
1569    return c_to_a_string (p, temp_string, DEFAULT_WIDTH);
1570  }
1571  
1572  //! @brief Add c to str, assuming that "str" is large enough.
1573  
1574  char *plusto (char c, char *str)
1575  {
1576    MOVE (&str[1], &str[0], (unt) (strlen (str) + 1));
1577    str[0] = c;
1578    return str;
1579  }
1580  
1581  //! @brief Add c to str, assuming that "str" is large enough.
1582  
1583  char *string_plusab_char (char *str, char c, int strwid)
1584  {
1585    char z[2];
1586    z[0] = c;
1587    z[1] = NULL_CHAR;
1588    a68_bufcat (str, z, strwid);
1589    return str;
1590  }
1591  
1592  //! @brief Add leading spaces to str until length is width.
1593  
1594  char *leading_spaces (char *str, int width)
1595  {
1596    int j = width - (int) strlen (str);
1597    while (--j >= 0) {
1598      (void) plusto (BLANK_CHAR, str);
1599    }
1600    return str;
1601  }
1602  
1603  //! @brief Convert int to char using a table.
1604  
1605  char digchar (int k)
1606  {
1607    char *s = "0123456789abcdefghijklmnopqrstuvwxyz";
1608    if (k >= 0 && k < (int) strlen (s)) {
1609      return s[k];
1610    } else {
1611      return ERROR_CHAR;
1612    }
1613  }
1614  
1615  //! @brief Formatted string for HEX_NUMBER.
1616  
1617  char *bits_to_string (NODE_T * p)
1618  {
1619    A68_INT width, base;
1620    POP_OBJECT (p, &base, A68_INT);
1621    POP_OBJECT (p, &width, A68_INT);
1622    DECREMENT_STACK_POINTER (p, SIZE (M_HEX_NUMBER));
1623    CHECK_INT_SHORTEN (p, VALUE (&base));
1624    CHECK_INT_SHORTEN (p, VALUE (&width));
1625    MOID_T *mode = (MOID_T *) (VALUE ((A68_UNION *) STACK_TOP));
1626    int length = ABS (VALUE (&width)), radix = ABS (VALUE (&base));
1627    if (radix < 2 || radix > 16) {
1628      diagnostic (A68_RUNTIME_ERROR, p, ERROR_INVALID_RADIX, radix);
1629      exit_genie (p, A68_RUNTIME_ERROR);
1630    }
1631    reset_transput_buffer (EDIT_BUFFER);
1632  #if (A68_LEVEL <= 2)
1633    (void) mode;
1634    (void) length;
1635    (void) error_chars (get_transput_buffer (EDIT_BUFFER), VALUE (&width));
1636  #else
1637    {
1638      BOOL_T ret = A68_TRUE;
1639      if (mode == M_BOOL) {
1640        UNSIGNED_T z = VALUE ((A68_BOOL *) (STACK_OFFSET (A68_UNION_SIZE)));
1641        ret = convert_radix (p, (UNSIGNED_T) z, radix, length);
1642      } else if (mode == M_CHAR) {
1643        INT_T z = VALUE ((A68_CHAR *) (STACK_OFFSET (A68_UNION_SIZE)));
1644        ret = convert_radix (p, (UNSIGNED_T) z, radix, length);
1645      } else if (mode == M_INT) {
1646        INT_T z = VALUE ((A68_INT *) (STACK_OFFSET (A68_UNION_SIZE)));
1647        ret = convert_radix (p, (UNSIGNED_T) z, radix, length);
1648      } else if (mode == M_REAL) {
1649  // A trick to copy a REAL into an unt without truncating
1650        UNSIGNED_T z;
1651        memcpy (&z, (void *) &VALUE ((A68_REAL *) (STACK_OFFSET (A68_UNION_SIZE))), 8);
1652        ret = convert_radix (p, z, radix, length);
1653      } else if (mode == M_BITS) {
1654        UNSIGNED_T z = VALUE ((A68_BITS *) (STACK_OFFSET (A68_UNION_SIZE)));
1655        ret = convert_radix (p, (UNSIGNED_T) z, radix, length);
1656      } else if (mode == M_LONG_INT) {
1657        DOUBLE_NUM_T z = VALUE ((A68_LONG_INT *) (STACK_OFFSET (A68_UNION_SIZE)));
1658        ret = convert_radix_double (p, z, radix, length);
1659      } else if (mode == M_LONG_REAL) {
1660        DOUBLE_NUM_T z = VALUE ((A68_LONG_REAL *) (STACK_OFFSET (A68_UNION_SIZE)));
1661        ret = convert_radix_double (p, z, radix, length);
1662      } else if (mode == M_LONG_BITS) {
1663        DOUBLE_NUM_T z = VALUE ((A68_LONG_BITS *) (STACK_OFFSET (A68_UNION_SIZE)));
1664        ret = convert_radix_double (p, z, radix, length);
1665      }
1666      if (ret == A68_FALSE) {
1667        errno = EDOM;
1668        PRELUDE_ERROR (A68_TRUE, p, ERROR_OUT_OF_BOUNDS, mode);
1669      }
1670    }
1671  #endif
1672    return get_transput_buffer (EDIT_BUFFER);
1673  }
1674  
1675  //! @brief Standard string for LONG INT.
1676  
1677  #if (A68_LEVEL >= 3)
1678  char *long_sub_whole_double (NODE_T * p, DOUBLE_NUM_T n, int width)
1679  {
1680    char *s = stack_string (p, 8 + width);
1681    DOUBLE_NUM_T ten;
1682    set_lw (ten, 10);
1683    s[0] = NULL_CHAR;
1684    int len = 0;
1685    do {
1686      if (len < width) {
1687        DOUBLE_NUM_T w = double_udiv (p, M_LONG_INT, n, ten, 1);
1688        (void) plusto (digchar (LW (w)), s);
1689      }
1690      len++;
1691      n = double_udiv (p, M_LONG_INT, n, ten, 0);
1692    } while (!D_ZERO (n));
1693    if (len > width) {
1694      (void) error_chars (s, width);
1695    }
1696    return s;
1697  }
1698  #endif
1699  
1700  char *long_sub_whole (NODE_T * p, MP_T * m, int digits, int width)
1701  {
1702    int len = 0;
1703    char *s = stack_string (p, 8 + width);
1704    s[0] = NULL_CHAR;
1705    ADDR_T pop_sp = A68_SP;
1706    MP_T *n = nil_mp (p, digits);
1707    (void) move_mp (n, m, digits);
1708    do {
1709      if (len < width) {
1710  // Sic transit gloria mundi.
1711        int n_mod_10 = (MP_INT_T) MP_DIGIT (n, (int) (1 + MP_EXPONENT (n))) % 10;
1712        (void) plusto (digchar (n_mod_10), s);
1713      }
1714      len++;
1715      (void) over_mp_digit (p, n, n, (MP_T) 10, digits);
1716    } while (MP_DIGIT (n, 1) > 0);
1717    if (len > width) {
1718      (void) error_chars (s, width);
1719    }
1720    A68_SP = pop_sp;
1721    return s;
1722  }
1723  
1724  //! @brief Standard string for INT.
1725  
1726  char *sub_whole (NODE_T * p, INT_T n, int width)
1727  {
1728    char *s = stack_string (p, 8 + width);
1729    int len = 0;
1730    s[0] = NULL_CHAR;
1731    do {
1732      if (len < width) {
1733        (void) plusto (digchar (n % 10), s);
1734      }
1735      len++;
1736      n /= 10;
1737    } while (n != 0);
1738    if (len > width) {
1739      (void) error_chars (s, width);
1740    }
1741    return s;
1742  }
1743  
1744  //! @brief Formatted string for NUMBER.
1745  
1746  char *whole (NODE_T * p)
1747  {
1748    A68_INT width;
1749    POP_OBJECT (p, &width, A68_INT);
1750    CHECK_INT_SHORTEN (p, VALUE (&width));
1751    int arg_sp = A68_SP;
1752    DECREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
1753    MOID_T *mode = (MOID_T *) (VALUE ((A68_UNION *) STACK_TOP));
1754    if (mode == M_INT) {
1755      INT_T x = VALUE ((A68_INT *) (STACK_OFFSET (A68_UNION_SIZE)));
1756      INT_T n = ABS (x);
1757      int length = ABS (VALUE (&width)) - (x < 0 || VALUE (&width) > 0 ? 1 : 0);
1758      int size = (x < 0 ? 1 : (VALUE (&width) > 0 ? 1 : 0));
1759      if (VALUE (&width) == 0) {
1760        INT_T m = n;
1761        length = 0;
1762        while ((m /= 10, length++, m != 0)) {
1763          ;
1764        }
1765      }
1766      size += length;
1767      size = 8 + (size > VALUE (&width) ? size : VALUE (&width));
1768      char *s = stack_string (p, size);
1769      a68_bufcpy (s, sub_whole (p, n, length), size);
1770      if (length == 0 || strchr (s, ERROR_CHAR) != NO_TEXT) {
1771        (void) error_chars (s, VALUE (&width));
1772      } else {
1773        if (x < 0) {
1774          (void) plusto ('-', s);
1775        } else if (VALUE (&width) > 0) {
1776          (void) plusto ('+', s);
1777        }
1778        if (VALUE (&width) != 0) {
1779          (void) leading_spaces (s, ABS (VALUE (&width)));
1780        }
1781      }
1782      return s;
1783    }
1784  #if (A68_LEVEL >= 3)
1785    if (mode == M_LONG_INT) {
1786      DOUBLE_NUM_T x = VALUE ((A68_LONG_INT *) (STACK_OFFSET (A68_UNION_SIZE))), n, ten;
1787      set_lw (ten, 10);
1788      n = abs_double_int (x);
1789      int length = ABS (VALUE (&width)) - (D_NEG (x) || VALUE (&width) > 0 ? 1 : 0);
1790      int size = (D_NEG (x) ? 1 : (VALUE (&width) > 0 ? 1 : 0));
1791      if (VALUE (&width) == 0) {
1792        DOUBLE_NUM_T m = n;
1793        length = 0;
1794        while ((m = double_udiv (p, M_LONG_INT, m, ten, 0), length++, !D_ZERO (m))) {
1795          ;
1796        }
1797      }
1798      size += length;
1799      size = 8 + (size > VALUE (&width) ? size : VALUE (&width));
1800      char *s = stack_string (p, size);
1801      a68_bufcpy (s, long_sub_whole_double (p, n, length), size);
1802      if (length == 0 || strchr (s, ERROR_CHAR) != NO_TEXT) {
1803        (void) error_chars (s, VALUE (&width));
1804      } else {
1805        if (D_NEG (x)) {
1806          (void) plusto ('-', s);
1807        } else if (VALUE (&width) > 0) {
1808          (void) plusto ('+', s);
1809        }
1810        if (VALUE (&width) != 0) {
1811          (void) leading_spaces (s, ABS (VALUE (&width)));
1812        }
1813      }
1814      return s;
1815    }
1816  #endif
1817    if (mode == M_LONG_INT || mode == M_LONG_LONG_INT) {
1818      int digits = DIGITS (mode);
1819      MP_T *n = (MP_T *) (STACK_OFFSET (A68_UNION_SIZE));
1820      A68_SP = arg_sp;            // We keep the mp where it's at
1821      if (MP_EXPONENT (n) >= (MP_T) digits) {
1822        int max_length = (mode == M_LONG_INT ? A68_LONG_INT_WIDTH : A68_LONG_LONG_INT_WIDTH);
1823        int length = (VALUE (&width) == 0 ? max_length : VALUE (&width));
1824        char *s = stack_string (p, 1 + length);
1825        (void) error_chars (s, length);
1826        return s;
1827      }
1828      BOOL_T ltz = (BOOL_T) (MP_DIGIT (n, 1) < 0);
1829      int length = ABS (VALUE (&width)) - (ltz || VALUE (&width) > 0 ? 1 : 0);
1830      int size = (ltz ? 1 : (VALUE (&width) > 0 ? 1 : 0));
1831      MP_DIGIT (n, 1) = ABS (MP_DIGIT (n, 1));
1832      if (VALUE (&width) == 0) {
1833        MP_T *m = nil_mp (p, digits);
1834        (void) move_mp (m, n, digits);
1835        length = 0;
1836        while ((over_mp_digit (p, m, m, (MP_T) 10, digits), length++, MP_DIGIT (m, 1) != 0)) {
1837          ;
1838        }
1839      }
1840      size += length;
1841      size = 8 + (size > VALUE (&width) ? size : VALUE (&width));
1842      char *s = stack_string (p, size);
1843      a68_bufcpy (s, long_sub_whole (p, n, digits, length), size);
1844      if (length == 0 || strchr (s, ERROR_CHAR) != NO_TEXT) {
1845        (void) error_chars (s, VALUE (&width));
1846      } else {
1847        if (ltz) {
1848          (void) plusto ('-', s);
1849        } else if (VALUE (&width) > 0) {
1850          (void) plusto ('+', s);
1851        }
1852        if (VALUE (&width) != 0) {
1853          (void) leading_spaces (s, ABS (VALUE (&width)));
1854        }
1855      }
1856      return s;
1857    }
1858    if (mode == M_REAL || mode == M_LONG_REAL || mode == M_LONG_LONG_REAL) {
1859      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
1860      PUSH_VALUE (p, VALUE (&width), A68_INT);
1861      PUSH_VALUE (p, 0, A68_INT);
1862      return fixed (p);
1863    }
1864    return NO_TEXT;
1865  }
1866  
1867  //! @brief Fetch next digit from LONG.
1868  
1869  char long_choose_dig (NODE_T * p, MP_T * y, int digits)
1870  {
1871  // Assuming positive "y".
1872    ADDR_T pop_sp = A68_SP;
1873    (void) mul_mp_digit (p, y, y, (MP_T) 10, digits);
1874    int c = MP_EXPONENT (y) == 0 ? (MP_INT_T) MP_DIGIT (y, 1) : 0;
1875    if (c > 9) {
1876      c = 9;
1877    }
1878    MP_T *t = lit_mp (p, c, 0, digits);
1879    (void) sub_mp (p, y, y, t, digits);
1880  // Reset the stack to prevent overflow, there may be many digits.
1881    A68_SP = pop_sp;
1882    return digchar (c);
1883  }
1884  
1885  //! @brief Standard string for LONG.
1886  
1887  char *long_sub_fixed (NODE_T * p, MP_T * x, int digits, int width, int after)
1888  {
1889    ADDR_T pop_sp = A68_SP;
1890    MP_T *y = nil_mp (p, digits);
1891    MP_T *s = nil_mp (p, digits);
1892    MP_T *t = nil_mp (p, digits);
1893    (void) ten_up_mp (p, t, -after, digits);
1894    (void) half_mp (p, t, t, digits);
1895    (void) add_mp (p, y, x, t, digits);
1896    int before = 0;
1897  // Not RR - argument reduction.
1898    while (MP_EXPONENT (y) > 1) {
1899      int k = (int) round (MP_EXPONENT (y) - 1);
1900      MP_EXPONENT (y) -= k;
1901      before += k * LOG_MP_RADIX;
1902    }
1903  // Follow RR again.
1904    SET_MP_ONE (s, digits);
1905    while ((sub_mp (p, t, y, s, digits), MP_DIGIT (t, 1) >= 0)) {
1906      before++;
1907      (void) div_mp_digit (p, y, y, (MP_T) 10, digits);
1908    }
1909  // Compose the number.
1910    if (before + after + (after > 0 ? 1 : 0) > width) {
1911      char *str = stack_string (p, width + 1);
1912      (void) error_chars (str, width);
1913      A68_SP = pop_sp;
1914      return str;
1915    }
1916    int strwid = 8 + before + after;
1917    char *str = stack_string (p, strwid);
1918    str[0] = NULL_CHAR;
1919    int len = 0;
1920    for (int j = 0; j < before; j++) {
1921      char ch = (char) (len < A68_LONG_LONG_REAL_WIDTH ? long_choose_dig (p, y, digits) : '0');
1922      (void) string_plusab_char (str, ch, strwid);
1923      len++;
1924    }
1925    if (after > 0) {
1926      (void) string_plusab_char (str, POINT_CHAR, strwid);
1927    }
1928    for (int j = 0; j < after; j++) {
1929      char ch = (char) (len < A68_LONG_LONG_REAL_WIDTH ? long_choose_dig (p, y, digits) : '0');
1930      (void) string_plusab_char (str, ch, strwid);
1931      len++;
1932    }
1933    if ((int) strlen (str) > width) {
1934      (void) error_chars (str, width);
1935    }
1936    A68_SP = pop_sp;
1937    return str;
1938  }
1939  
1940  #if (A68_LEVEL >= 3)
1941  
1942  //! @brief Fetch next digit from REAL.
1943  
1944  char choose_dig_double (DOUBLE_T * y)
1945  {
1946  // Assuming positive "y".
1947    int c = (int) (*y *= 10);
1948    if (c > 9) {
1949      c = 9;
1950    }
1951    *y -= c;
1952    return digchar (c);
1953  }
1954  
1955  #endif
1956  
1957  #if (A68_LEVEL >= 3)
1958  
1959  //! @brief Standard string for REAL.
1960  
1961  char *sub_fixed_double (NODE_T * p, DOUBLE_T x, int width, int after, int precision)
1962  {
1963    ABEND (x < 0, ERROR_INTERNAL_CONSISTENCY, __func__);
1964  // Round and scale. 
1965    DOUBLE_T z = x + 0.5q * ten_up_double (-after);
1966    DOUBLE_T y = z;
1967    int before = 0;
1968  // Not according RR - argument reduction to avoid long division loop.
1969    if (z >= 1.0e10q) {          // Arbitrary, log10 must be worthwhile.
1970      before = (int) floor_double (log10_double (z)) - 1;
1971      z /= ten_up_double (before);
1972    }
1973  // Follow RR again.
1974    while (z >= 1.0q) {
1975      before++;
1976      z /= 10.0q;
1977    }
1978  // Scale number.
1979    y /= ten_up_double (before);
1980  // Put digits, prevent garbage from overstretching precision.
1981  // Many languages produce garbage when specifying more decimals 
1982  // than the type actually has. A68G pads '0's in this case.
1983  // That is just as arbitrary, but at least recognisable.
1984    int strwid = 8 + before + after;      // A bit too long.
1985    char *str = stack_string (p, strwid);
1986    int len = 0;
1987    for (int j = 0; j < before; j++) {
1988      char ch = (char) (len < precision ? choose_dig_double (&y) : '0');
1989      (void) string_plusab_char (str, ch, strwid);
1990      len++;
1991    }
1992    if (after > 0) {
1993      (void) string_plusab_char (str, POINT_CHAR, strwid);
1994    }
1995    for (int j = 0; j < after; j++) {
1996      char ch = (char) (len < precision ? choose_dig_double (&y) : '0');
1997      (void) string_plusab_char (str, ch, strwid);
1998      len++;
1999    }
2000    if ((int) strlen (str) > width) {
2001      (void) error_chars (str, width);
2002    }
2003    return str;
2004  }
2005  
2006  //! @brief Standard string for REAL.
2007  
2008  char *sub_fixed (NODE_T * p, REAL_T x, int width, int after)
2009  {
2010  // Better precision than the REAL only routine
2011    return sub_fixed_double (p, (DOUBLE_T) x, width, after, A68_REAL_WIDTH);
2012  }
2013  
2014  #else
2015  
2016  //! @brief Fetch next digit from REAL.
2017  
2018  char choose_dig (REAL_T * y)
2019  {
2020  // Assuming positive "y".
2021    int c = (int) (*y *= 10);
2022    if (c > 9) {
2023      c = 9;
2024    }
2025    *y -= c;
2026    return digchar (c);
2027  }
2028  
2029  //! @brief Standard string for REAL.
2030  
2031  char *sub_fixed (NODE_T * p, REAL_T x, int width, int after)
2032  {
2033    ABEND (x < 0, ERROR_INTERNAL_CONSISTENCY, __func__);
2034  // Round and scale. 
2035    REAL_T z = x + 0.5 * ten_up (-after);
2036    REAL_T y = z;
2037    int before = 0;
2038  // Not according RR - argument reduction to avoid long division loop.
2039    if (z >= 1.0e10) {            // Arbitrary, log10 must be worthwhile.
2040      before = (int) floor (log10 (z)) - 1;
2041      z /= ten_up (before);
2042    }
2043  // Follow RR again.
2044    while (z >= 1.0) {
2045      before++;
2046      z /= 10.0;
2047    }
2048  // Scale number.
2049    y /= ten_up (before);
2050  // Put digits, prevent garbage from overstretching precision.
2051  // Many languages produce garbage when specifying more decimals 
2052  // than the type actually has. A68G pads '0's in this case.
2053  // That is just as arbitrary, but at least recognisable.
2054    int strwid = 8 + before + after;      // A bit too long.
2055    char *str = stack_string (p, strwid);
2056    int len = 0;
2057    for (int j = 0; j < before; j++) {
2058      char ch = (char) (len < A68_REAL_WIDTH ? choose_dig (&y) : '0');
2059      (void) string_plusab_char (str, ch, strwid);
2060      len++;
2061    }
2062    if (after > 0) {
2063      (void) string_plusab_char (str, POINT_CHAR, strwid);
2064    }
2065    for (int j = 0; j < after; j++) {
2066      char ch = (char) (len < A68_REAL_WIDTH ? choose_dig (&y) : '0');
2067      (void) string_plusab_char (str, ch, strwid);
2068      len++;
2069    }
2070    if ((int) strlen (str) > width) {
2071      (void) error_chars (str, width);
2072    }
2073    return str;
2074  }
2075  
2076  #endif
2077  
2078  //! @brief Formatted string for NUMBER.
2079  
2080  char *fixed (NODE_T * p)
2081  {
2082    A68_INT width, after;
2083    POP_OBJECT (p, &after, A68_INT);
2084    POP_OBJECT (p, &width, A68_INT);
2085    CHECK_INT_SHORTEN (p, VALUE (&after));
2086    CHECK_INT_SHORTEN (p, VALUE (&width));
2087    ADDR_T arg_sp = A68_SP;
2088    DECREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
2089    MOID_T *mode = (MOID_T *) (VALUE ((A68_UNION *) STACK_TOP));
2090    ADDR_T pop_sp = A68_SP;
2091    if (mode == M_REAL) {
2092      REAL_T x = VALUE ((A68_REAL *) (STACK_OFFSET (A68_UNION_SIZE)));
2093      int length = ABS (VALUE (&width)) - (x < 0 || VALUE (&width) > 0 ? 1 : 0);
2094      CHECK_REAL (p, x);
2095      A68_SP = arg_sp;
2096      if (VALUE (&after) >= 0 && (length > VALUE (&after) || VALUE (&width) == 0)) {
2097        REAL_T y = ABS (x);
2098        if (VALUE (&width) == 0) {
2099          length = (VALUE (&after) == 0 ? 1 : 0);
2100          REAL_T z0 = ten_up (-VALUE (&after)), z1 = ten_up (length);
2101          while (y + 0.5 * z0 > z1) {
2102            length++;
2103            z1 *= 10.0;
2104          }
2105          length += (VALUE (&after) == 0 ? 0 : VALUE (&after) + 1);
2106        }
2107        char *s = sub_fixed (p, y, length, VALUE (&after));
2108        if (strchr (s, ERROR_CHAR) == NO_TEXT) {
2109          if (length > (int) strlen (s) && (s[0] != NULL_CHAR ? s[0] == POINT_CHAR : A68_TRUE) && y < 1.0) {
2110            (void) plusto ('0', s);
2111          }
2112          if (x < 0) {
2113            (void) plusto ('-', s);
2114          } else if (VALUE (&width) > 0) {
2115            (void) plusto ('+', s);
2116          }
2117          if (VALUE (&width) != 0) {
2118            (void) leading_spaces (s, ABS (VALUE (&width)));
2119          }
2120          return s;
2121        } else if (VALUE (&after) > 0) {
2122          A68_SP = arg_sp;
2123          PUSH_VALUE (p, VALUE (&width), A68_INT);
2124          PUSH_VALUE (p, VALUE (&after) - 1, A68_INT);
2125          return fixed (p);
2126        } else {
2127          return error_chars (s, VALUE (&width));
2128        }
2129      } else {
2130        char *s = stack_string (p, 8 + ABS (VALUE (&width)));
2131        return error_chars (s, VALUE (&width));
2132      }
2133    }
2134  #if (A68_LEVEL >= 3)
2135    if (mode == M_LONG_REAL) {
2136      DOUBLE_T x = VALUE ((A68_LONG_REAL *) (STACK_OFFSET (A68_UNION_SIZE))).f;
2137      int length = ABS (VALUE (&width)) - (x < 0 || VALUE (&width) > 0 ? 1 : 0);
2138      CHECK_DOUBLE_REAL (p, x);
2139      A68_SP = arg_sp;
2140      if (VALUE (&after) >= 0 && (length > VALUE (&after) || VALUE (&width) == 0)) {
2141        DOUBLE_T y = ABS (x);
2142        if (VALUE (&width) == 0) {
2143          length = (VALUE (&after) == 0 ? 1 : 0);
2144          DOUBLE_T z0 = ten_up_double (-VALUE (&after)), z1 = ten_up_double (length);
2145          while (y + 0.5 * z0 > z1) {
2146            length++;
2147            z1 *= 10.0;
2148          }
2149          length += (VALUE (&after) == 0 ? 0 : VALUE (&after) + 1);
2150        }
2151        char *s = sub_fixed_double (p, y, length, VALUE (&after), A68_LONG_REAL_WIDTH);
2152        if (strchr (s, ERROR_CHAR) == NO_TEXT) {
2153          if (length > (int) strlen (s) && (s[0] != NULL_CHAR ? s[0] == POINT_CHAR : A68_TRUE) && y < 1.0) {
2154            (void) plusto ('0', s);
2155          }
2156          if (x < 0) {
2157            (void) plusto ('-', s);
2158          } else if (VALUE (&width) > 0) {
2159            (void) plusto ('+', s);
2160          }
2161          if (VALUE (&width) != 0) {
2162            (void) leading_spaces (s, ABS (VALUE (&width)));
2163          }
2164          return s;
2165        } else if (VALUE (&after) > 0) {
2166          A68_SP = arg_sp;
2167          PUSH_VALUE (p, VALUE (&width), A68_INT);
2168          PUSH_VALUE (p, VALUE (&after) - 1, A68_INT);
2169          return fixed (p);
2170        } else {
2171          return error_chars (s, VALUE (&width));
2172        }
2173      } else {
2174        char *s = stack_string (p, 8 + ABS (VALUE (&width)));
2175        return error_chars (s, VALUE (&width));
2176      }
2177    }
2178  #endif
2179    if (mode == M_LONG_REAL || mode == M_LONG_LONG_REAL) {
2180      int digits = DIGITS (mode);
2181      MP_T *x = (MP_T *) (STACK_OFFSET (A68_UNION_SIZE));
2182      A68_SP = arg_sp;
2183      BOOL_T ltz = (BOOL_T) (MP_DIGIT (x, 1) < 0);
2184      MP_DIGIT (x, 1) = ABS (MP_DIGIT (x, 1));
2185      int length = ABS (VALUE (&width)) - (ltz || VALUE (&width) > 0 ? 1 : 0);
2186      if (VALUE (&after) >= 0 && (length > VALUE (&after) || VALUE (&width) == 0)) {
2187        MP_T *z0 = nil_mp (p, digits);
2188        MP_T *z1 = nil_mp (p, digits);
2189        MP_T *t = nil_mp (p, digits);
2190        if (VALUE (&width) == 0) {
2191          length = (VALUE (&after) == 0 ? 1 : 0);
2192          (void) set_mp (z0, (MP_T) (MP_RADIX / 10), -1, digits);
2193          (void) set_mp (z1, (MP_T) 10, 0, digits);
2194          (void) pow_mp_int (p, z0, z0, VALUE (&after), digits);
2195          (void) pow_mp_int (p, z1, z1, length, digits);
2196          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)) {
2197            length++;
2198            (void) mul_mp_digit (p, z1, z1, (MP_T) 10, digits);
2199          }
2200          length += (VALUE (&after) == 0 ? 0 : VALUE (&after) + 1);
2201        }
2202  //    char *s = stack_string (p, 8 + length);
2203        char *s = long_sub_fixed (p, x, digits, length, VALUE (&after));
2204        if (strchr (s, ERROR_CHAR) == NO_TEXT) {
2205          if (length > (int) strlen (s) && (s[0] != NULL_CHAR ? s[0] == POINT_CHAR : A68_TRUE) && (MP_EXPONENT (x) < 0 || MP_DIGIT (x, 1) == 0)) {
2206            (void) plusto ('0', s);
2207          }
2208          if (ltz) {
2209            (void) plusto ('-', s);
2210          } else if (VALUE (&width) > 0) {
2211            (void) plusto ('+', s);
2212          }
2213          if (VALUE (&width) != 0) {
2214            (void) leading_spaces (s, ABS (VALUE (&width)));
2215          }
2216          return s;
2217        } else if (VALUE (&after) > 0) {
2218          A68_SP = arg_sp;
2219          MP_DIGIT (x, 1) = ltz ? -ABS (MP_DIGIT (x, 1)) : ABS (MP_DIGIT (x, 1));
2220          PUSH_VALUE (p, VALUE (&width), A68_INT);
2221          PUSH_VALUE (p, VALUE (&after) - 1, A68_INT);
2222          return fixed (p);
2223        } else {
2224          return error_chars (s, VALUE (&width));
2225        }
2226      } else {
2227        char *s = stack_string (p, 8 + ABS (VALUE (&width)));
2228        return error_chars (s, VALUE (&width));
2229      }
2230    }
2231    if (mode == M_INT) {
2232      int x = VALUE ((A68_INT *) (STACK_OFFSET (A68_UNION_SIZE)));
2233      PUSH_UNION (p, M_REAL);
2234      PUSH_VALUE (p, (REAL_T) x, A68_REAL);
2235      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_REAL)));
2236      PUSH_VALUE (p, VALUE (&width), A68_INT);
2237      PUSH_VALUE (p, VALUE (&after), A68_INT);
2238      return fixed (p);
2239    }
2240    if (mode == M_LONG_INT || mode == M_LONG_LONG_INT) {
2241      A68_SP = pop_sp;
2242      if (mode == M_LONG_INT) {
2243        VALUE ((A68_UNION *) STACK_TOP) = (void *) M_LONG_REAL;
2244      } else {
2245        VALUE ((A68_UNION *) STACK_TOP) = (void *) M_LONG_LONG_REAL;
2246      } INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
2247      PUSH_VALUE (p, VALUE (&width), A68_INT);
2248      PUSH_VALUE (p, VALUE (&after), A68_INT);
2249      return fixed (p);
2250    }
2251    return NO_TEXT;
2252  }
2253  
2254  //! @brief Scale LONG for formatting.
2255  
2256  void long_standardise (NODE_T * p, MP_T * y, int digits, int before, int after, int *q)
2257  {
2258    ADDR_T pop_sp = A68_SP;
2259    MP_T *f = nil_mp (p, digits);
2260    MP_T *g = nil_mp (p, digits);
2261    MP_T *h = nil_mp (p, digits);
2262    MP_T *t = nil_mp (p, digits);
2263    ten_up_mp (p, g, before, digits);
2264    (void) div_mp_digit (p, h, g, (MP_T) 10, digits);
2265  // Speed huge exponents.
2266    if ((MP_EXPONENT (y) - MP_EXPONENT (g)) > 1) {
2267      (*q) += LOG_MP_RADIX * ((int) MP_EXPONENT (y) - (int) MP_EXPONENT (g) - 1);
2268      MP_EXPONENT (y) = MP_EXPONENT (g) + 1;
2269    }
2270    while ((sub_mp (p, t, y, g, digits), MP_DIGIT (t, 1) >= 0)) {
2271      (void) div_mp_digit (p, y, y, (MP_T) 10, digits);
2272      (*q)++;
2273    }
2274    if (MP_DIGIT (y, 1) != 0) {
2275  // Speed huge exponents.
2276      if ((MP_EXPONENT (y) - MP_EXPONENT (h)) < -1) {
2277        (*q) -= LOG_MP_RADIX * ((int) MP_EXPONENT (h) - (int) MP_EXPONENT (y) - 1);
2278        MP_EXPONENT (y) = MP_EXPONENT (h) - 1;
2279      }
2280      while ((sub_mp (p, t, y, h, digits), MP_DIGIT (t, 1) < 0)) {
2281        (void) mul_mp_digit (p, y, y, (MP_T) 10, digits);
2282        (*q)--;
2283      }
2284    }
2285    ten_up_mp (p, f, -after, digits);
2286    (void) div_mp_digit (p, t, f, (MP_T) 2, digits);
2287    (void) add_mp (p, t, y, t, digits);
2288    (void) sub_mp (p, t, t, g, digits);
2289    if (MP_DIGIT (t, 1) >= 0) {
2290      (void) move_mp (y, h, digits);
2291      (*q)++;
2292    }
2293    A68_SP = pop_sp;
2294  }
2295  
2296  #if (A68_LEVEL >= 3)
2297  
2298  //! @brief Scale REAL for formatting.
2299  
2300  void standardise_double (DOUBLE_T * y, int before, int after, int *p)
2301  {
2302  //int g = 1.0q; for (int j = 0; j < before; j++) { g *= 10.0q; }
2303    DOUBLE_T g = ten_up_double (before);
2304    DOUBLE_T h = g / 10.0q;
2305    while (*y >= g) {
2306      *y *= 0.1q;
2307      (*p)++;
2308    }
2309    if (*y != 0.0q) {
2310      while (*y < h) {
2311        *y *= 10.0q;
2312        (*p)--;
2313      }
2314    }
2315  //f = 1.0q; for (int j = 0; j < after; j++) { f *= 0.1q; }
2316    DOUBLE_T f = ten_up_double (-after);
2317    if (*y + 0.5q * f >= g) {
2318      *y = h;
2319      (*p)++;
2320    }
2321  }
2322  
2323  //! @brief Scale REAL for formatting.
2324  
2325  void standardise (REAL_T * y, int before, int after, int *p)
2326  {
2327  // Better precision than the REAL only routine
2328    DOUBLE_T z = (DOUBLE_T) * y;
2329    standardise_double (&z, before, after, p);
2330    *y = (REAL_T) z;
2331  }
2332  
2333  #else
2334  
2335  //! @brief Scale REAL for formatting.
2336  
2337  void standardise (REAL_T * y, int before, int after, int *p)
2338  {
2339  // This according RR, but for REAL the last digits are approximate.
2340  // A68G 3 uses DOUBLE precision version.
2341  //int g = 1.0; for (int j = 0; j < before; j++) { g *= 10.0; }
2342    REAL_T g = ten_up (before);
2343    REAL_T h = g / 10.0;
2344    while (*y >= g) {
2345      *y *= 0.1;
2346      (*p)++;
2347    }
2348    if (*y != 0.0) {
2349      while (*y < h) {
2350        *y *= 10.0;
2351        (*p)--;
2352      }
2353    }
2354  //f = 1.0; for (int j = 0; j < after; j++) { f *= 0.1; }
2355    REAL_T f = ten_up (-after);
2356    if (*y + 0.5 * f >= g) {
2357      *y = h;
2358      (*p)++;
2359    }
2360  }
2361  
2362  #endif
2363  
2364  //! @brief Formatted string for NUMBER.
2365  
2366  char *real (NODE_T * p)
2367  {
2368  // POP arguments.
2369    A68_INT width, after, expo, frmt;
2370    POP_OBJECT (p, &frmt, A68_INT);
2371    POP_OBJECT (p, &expo, A68_INT);
2372    POP_OBJECT (p, &after, A68_INT);
2373    POP_OBJECT (p, &width, A68_INT);
2374    CHECK_INT_SHORTEN (p, VALUE (&frmt));
2375    CHECK_INT_SHORTEN (p, VALUE (&expo));
2376    CHECK_INT_SHORTEN (p, VALUE (&after));
2377    CHECK_INT_SHORTEN (p, VALUE (&width));
2378    ADDR_T arg_sp = A68_SP;
2379    DECREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
2380    MOID_T *mode = (MOID_T *) (VALUE ((A68_UNION *) STACK_TOP));
2381    ADDR_T pop_sp = A68_SP;
2382    if (mode == M_REAL) {
2383      REAL_T x = VALUE ((A68_REAL *) (STACK_OFFSET (A68_UNION_SIZE)));
2384      int before = ABS (VALUE (&width)) - ABS (VALUE (&expo)) - (VALUE (&after) != 0 ? VALUE (&after) + 1 : 0) - 2;
2385      A68_SP = arg_sp;
2386      CHECK_REAL (p, x);
2387      if (SIGN (before) + SIGN (VALUE (&after)) > 0) {
2388        REAL_T y = ABS (x);
2389        int q = 0;
2390        standardise (&y, before, VALUE (&after), &q);
2391        if (VALUE (&frmt) > 0) {
2392          while (q % VALUE (&frmt) != 0) {
2393            y *= 10;
2394            q--;
2395            if (VALUE (&after) > 0) {
2396              VALUE (&after)--;
2397            }
2398          }
2399        } else {
2400          REAL_T upb = ten_up (-VALUE (&frmt)), lwb = ten_up (-VALUE (&frmt) - 1);
2401          while (y < lwb) {
2402            y *= 10;
2403            q--;
2404            if (VALUE (&after) > 0) {
2405              VALUE (&after)--;
2406            }
2407          }
2408          while (y > upb) {
2409            y /= 10;
2410            q++;
2411            if (VALUE (&after) > 0) {
2412              VALUE (&after)++;
2413            }
2414          }
2415        }
2416        PUSH_UNION (p, M_REAL);
2417        PUSH_VALUE (p, SIGN (x) * y, A68_REAL);
2418        INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_REAL)));
2419        PUSH_VALUE (p, SIGN (VALUE (&width)) * (ABS (VALUE (&width)) - ABS (VALUE (&expo)) - 1), A68_INT);
2420        PUSH_VALUE (p, VALUE (&after), A68_INT);
2421        char *t1 = fixed (p);
2422        PUSH_UNION (p, M_INT);
2423        PUSH_VALUE (p, q, A68_INT);
2424        INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_INT)));
2425        PUSH_VALUE (p, VALUE (&expo), A68_INT);
2426        char *t2 = whole (p);
2427        int strwid = 8 + (int) strlen (t1) + 1 + (int) strlen (t2);
2428        char *s = stack_string (p, strwid);
2429        a68_bufcpy (s, t1, strwid);
2430        (void) string_plusab_char (s, EXPONENT_CHAR, strwid);
2431        a68_bufcat (s, t2, strwid);
2432        if (VALUE (&expo) == 0 || strchr (s, ERROR_CHAR) != NO_TEXT) {
2433          A68_SP = arg_sp;
2434          PUSH_VALUE (p, VALUE (&width), A68_INT);
2435          PUSH_VALUE (p, VALUE (&after) != 0 ? VALUE (&after) - 1 : 0, A68_INT);
2436          PUSH_VALUE (p, VALUE (&expo) > 0 ? VALUE (&expo) + 1 : VALUE (&expo) - 1, A68_INT);
2437          PUSH_VALUE (p, VALUE (&frmt), A68_INT);
2438          return real (p);
2439        } else {
2440          return s;
2441        }
2442      } else {
2443        char *s = stack_string (p, 8 + ABS (VALUE (&width)));
2444        return error_chars (s, VALUE (&width));
2445      }
2446    }
2447  #if (A68_LEVEL >= 3)
2448    if (mode == M_LONG_REAL) {
2449      DOUBLE_T x = VALUE ((A68_LONG_REAL *) (STACK_OFFSET (A68_UNION_SIZE))).f;
2450      int before = ABS (VALUE (&width)) - ABS (VALUE (&expo)) - (VALUE (&after) != 0 ? VALUE (&after) + 1 : 0) - 2;
2451      CHECK_DOUBLE_REAL (p, x);
2452      A68_SP = arg_sp;
2453      if (SIGN (before) + SIGN (VALUE (&after)) > 0) {
2454        DOUBLE_T y = (x >= 0.0q ? x : -x);
2455        int q = 0;
2456        standardise_double (&y, before, VALUE (&after), &q);
2457        if (VALUE (&frmt) > 0) {
2458          while (q % VALUE (&frmt) != 0) {
2459            y *= 10.0q;
2460            q--;
2461            if (VALUE (&after) > 0) {
2462              VALUE (&after)--;
2463            }
2464          }
2465        } else {
2466          DOUBLE_T upb = ten_up_double (-VALUE (&frmt)), lwb = ten_up_double (-VALUE (&frmt) - 1);
2467          while (y < lwb) {
2468            y *= 10.0q;
2469            q--;
2470            if (VALUE (&after) > 0) {
2471              VALUE (&after)--;
2472            }
2473          }
2474          while (y > upb) {
2475            y /= 10.0q;
2476            q++;
2477            if (VALUE (&after) > 0) {
2478              VALUE (&after)++;
2479            }
2480          }
2481        }
2482        PUSH_UNION (p, M_LONG_REAL);
2483        {
2484          DOUBLE_NUM_T d;
2485          d.f = (x >= 0.0q ? y : -y);
2486          PUSH_VALUE (p, d, A68_LONG_REAL);
2487        }
2488        INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_LONG_REAL)));
2489        PUSH_VALUE (p, SIGN (VALUE (&width)) * (ABS (VALUE (&width)) - ABS (VALUE (&expo)) - 1), A68_INT);
2490        PUSH_VALUE (p, VALUE (&after), A68_INT);
2491        char *t1 = fixed (p);
2492        PUSH_UNION (p, M_INT);
2493        PUSH_VALUE (p, q, A68_INT);
2494        INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_INT)));
2495        PUSH_VALUE (p, VALUE (&expo), A68_INT);
2496        char *t2 = whole (p);
2497        int strwid = 8 + (int) strlen (t1) + 1 + (int) strlen (t2);
2498        char *s = stack_string (p, strwid);
2499        a68_bufcpy (s, t1, strwid);
2500        (void) string_plusab_char (s, EXPONENT_CHAR, strwid);
2501        a68_bufcat (s, t2, strwid);
2502        if (VALUE (&expo) == 0 || strchr (s, ERROR_CHAR) != NO_TEXT) {
2503          A68_SP = arg_sp;
2504          PUSH_VALUE (p, VALUE (&width), A68_INT);
2505          PUSH_VALUE (p, VALUE (&after) != 0 ? VALUE (&after) - 1 : 0, A68_INT);
2506          PUSH_VALUE (p, VALUE (&expo) > 0 ? VALUE (&expo) + 1 : VALUE (&expo) - 1, A68_INT);
2507          PUSH_VALUE (p, VALUE (&frmt), A68_INT);
2508          return real (p);
2509        } else {
2510          return s;
2511        }
2512      } else {
2513        char *s = stack_string (p, 8 + ABS (VALUE (&width)));
2514        return error_chars (s, VALUE (&width));
2515      }
2516    }
2517  #endif
2518    if (mode == M_LONG_REAL || mode == M_LONG_LONG_REAL) {
2519      int digits = DIGITS (mode);
2520      int before;
2521      MP_T *x = (MP_T *) (STACK_OFFSET (A68_UNION_SIZE));
2522      CHECK_LONG_REAL (p, x, mode);
2523      BOOL_T ltz = (BOOL_T) (MP_DIGIT (x, 1) < 0);
2524      A68_SP = arg_sp;
2525      MP_DIGIT (x, 1) = ABS (MP_DIGIT (x, 1));
2526      before = ABS (VALUE (&width)) - ABS (VALUE (&expo)) - (VALUE (&after) != 0 ? VALUE (&after) + 1 : 0) - 2;
2527      if (SIGN (before) + SIGN (VALUE (&after)) > 0) {
2528        int q = 0;
2529        size_t N_mp = SIZE_MP (digits);
2530        MP_T *z = nil_mp (p, digits);
2531        (void) move_mp (z, x, digits);
2532        long_standardise (p, z, digits, before, VALUE (&after), &q);
2533        if (VALUE (&frmt) > 0) {
2534          while (q % VALUE (&frmt) != 0) {
2535            (void) mul_mp_digit (p, z, z, (MP_T) 10, digits);
2536            q--;
2537            if (VALUE (&after) > 0) {
2538              VALUE (&after)--;
2539            }
2540          }
2541        } else {
2542          ADDR_T sp1 = A68_SP;
2543          MP_T *dif = nil_mp (p, digits);
2544          MP_T *lim = nil_mp (p, digits);
2545          (void) ten_up_mp (p, lim, -VALUE (&frmt) - 1, digits);
2546          (void) sub_mp (p, dif, z, lim, digits);
2547          while (MP_DIGIT (dif, 1) < 0) {
2548            (void) mul_mp_digit (p, z, z, (MP_T) 10, digits);
2549            q--;
2550            if (VALUE (&after) > 0) {
2551              VALUE (&after)--;
2552            }
2553            (void) sub_mp (p, dif, z, lim, digits);
2554          }
2555          (void) mul_mp_digit (p, lim, lim, (MP_T) 10, digits);
2556          (void) sub_mp (p, dif, z, lim, digits);
2557          while (MP_DIGIT (dif, 1) > 0) {
2558            (void) div_mp_digit (p, z, z, (MP_T) 10, digits);
2559            q++;
2560            if (VALUE (&after) > 0) {
2561              VALUE (&after)++;
2562            }
2563            (void) sub_mp (p, dif, z, lim, digits);
2564          }
2565          A68_SP = sp1;
2566        }
2567        PUSH_UNION (p, mode);
2568        MP_DIGIT (z, 1) = (ltz ? -MP_DIGIT (z, 1) : MP_DIGIT (z, 1));
2569        PUSH (p, z, N_mp);
2570        INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE_MP (digits)));
2571        PUSH_VALUE (p, SIGN (VALUE (&width)) * (ABS (VALUE (&width)) - ABS (VALUE (&expo)) - 1), A68_INT);
2572        PUSH_VALUE (p, VALUE (&after), A68_INT);
2573        char *t1 = fixed (p);
2574        PUSH_UNION (p, M_INT);
2575        PUSH_VALUE (p, q, A68_INT);
2576        INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_INT)));
2577        PUSH_VALUE (p, VALUE (&expo), A68_INT);
2578        char *t2 = whole (p);
2579        int strwid = 8 + (int) strlen (t1) + 1 + (int) strlen (t2);
2580        char *s = stack_string (p, strwid);
2581        a68_bufcpy (s, t1, strwid);
2582        (void) string_plusab_char (s, EXPONENT_CHAR, strwid);
2583        a68_bufcat (s, t2, strwid);
2584        if (VALUE (&expo) == 0 || strchr (s, ERROR_CHAR) != NO_TEXT) {
2585          A68_SP = arg_sp;
2586          PUSH_VALUE (p, VALUE (&width), A68_INT);
2587          PUSH_VALUE (p, VALUE (&after) != 0 ? VALUE (&after) - 1 : 0, A68_INT);
2588          PUSH_VALUE (p, VALUE (&expo) > 0 ? VALUE (&expo) + 1 : VALUE (&expo) - 1, A68_INT);
2589          PUSH_VALUE (p, VALUE (&frmt), A68_INT);
2590          return real (p);
2591        } else {
2592          return s;
2593        }
2594      } else {
2595        char *s = stack_string (p, 8 + ABS (VALUE (&width)));
2596        return error_chars (s, VALUE (&width));
2597      }
2598    }
2599    if (mode == M_INT) {
2600      int x = VALUE ((A68_INT *) (STACK_OFFSET (A68_UNION_SIZE)));
2601      PUSH_UNION (p, M_REAL);
2602      PUSH_VALUE (p, (REAL_T) x, A68_REAL);
2603      INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER) - (A68_UNION_SIZE + SIZE (M_REAL)));
2604      PUSH_VALUE (p, VALUE (&width), A68_INT);
2605      PUSH_VALUE (p, VALUE (&after), A68_INT);
2606      PUSH_VALUE (p, VALUE (&expo), A68_INT);
2607      PUSH_VALUE (p, VALUE (&frmt), A68_INT);
2608      return real (p);
2609    }
2610    if (mode == M_LONG_INT || mode == M_LONG_LONG_INT) {
2611      A68_SP = pop_sp;
2612      if (mode == M_LONG_INT) {
2613        VALUE ((A68_UNION *) STACK_TOP) = (void *) M_LONG_REAL;
2614      } else {
2615        VALUE ((A68_UNION *) STACK_TOP) = (void *) M_LONG_LONG_REAL;
2616      } INCREMENT_STACK_POINTER (p, SIZE (M_NUMBER));
2617      PUSH_VALUE (p, VALUE (&width), A68_INT);
2618      PUSH_VALUE (p, VALUE (&after), A68_INT);
2619      PUSH_VALUE (p, VALUE (&expo), A68_INT);
2620      PUSH_VALUE (p, VALUE (&frmt), A68_INT);
2621      return real (p);
2622    }
2623    return NO_TEXT;
2624  }
2625  
2626  //! @brief PROC (NUMBER, INT) STRING whole
2627  
2628  void genie_whole (NODE_T * p)
2629  {
2630    ADDR_T pop_sp = A68_SP;
2631    char *str = whole (p);
2632    A68_SP = pop_sp - SIZE (M_INT) - SIZE (M_NUMBER);
2633    A68_REF ref = tmp_to_a68_string (p, str);
2634    PUSH_REF (p, ref);
2635  }
2636  
2637  //! @brief PROC (NUMBER, INT, INT) STRING bits 
2638  
2639  void genie_bits (NODE_T * p)
2640  {
2641    ADDR_T pop_sp = A68_SP;
2642    char *str = bits_to_string (p);
2643    A68_SP = pop_sp - 2 * SIZE (M_INT) - SIZE (M_HEX_NUMBER);
2644    A68_REF ref = tmp_to_a68_string (p, str);
2645    PUSH_REF (p, ref);
2646  }
2647  
2648  //! @brief PROC (NUMBER, INT, INT) STRING fixed
2649  
2650  void genie_fixed (NODE_T * p)
2651  {
2652    ADDR_T pop_sp = A68_SP;
2653    char *str = fixed (p);
2654    A68_SP = pop_sp - 2 * SIZE (M_INT) - SIZE (M_NUMBER);
2655    A68_REF ref = tmp_to_a68_string (p, str);
2656    PUSH_REF (p, ref);
2657  }
2658  
2659  //! @brief PROC (NUMBER, INT, INT, INT) STRING eng
2660  
2661  void genie_real (NODE_T * p)
2662  {
2663    ADDR_T pop_sp = A68_SP;
2664    char *str = real (p);
2665    A68_SP = pop_sp - 4 * SIZE (M_INT) - SIZE (M_NUMBER);
2666    A68_REF ref = tmp_to_a68_string (p, str);
2667    PUSH_REF (p, ref);
2668  }
2669  
2670  //! @brief PROC (NUMBER, INT, INT, INT) STRING float
2671  
2672  void genie_float (NODE_T * p)
2673  {
2674    PUSH_VALUE (p, 1, A68_INT);
2675    genie_real (p);
2676  }
2677  
2678  // ALGOL68C routines.
2679  
2680  //! @def A68C_TRANSPUT
2681  //! @brief Generate Algol68C routines readint, getint, etcetera.
2682  
2683  #define A68C_TRANSPUT(n, m)\
2684   void genie_get_##n (NODE_T * p)\
2685    {\
2686      A68_REF ref_file;\
2687      POP_REF (p, &ref_file);\
2688      CHECK_REF (p, ref_file, M_REF_FILE);\
2689      BYTE_T *z = STACK_TOP;\
2690      INCREMENT_STACK_POINTER (p, SIZE (MODE (m)));\
2691      ADDR_T pop_sp = A68_SP;\
2692      open_for_reading (p, ref_file);\
2693      genie_read_standard (p, MODE (m), z, ref_file);\
2694      A68_SP = pop_sp;\
2695    }\
2696    void genie_put_##n (NODE_T * p)\
2697    {\
2698      int size = SIZE (MODE (m)), sizf = SIZE (M_REF_FILE);\
2699      A68_REF ref_file = * (A68_REF *) STACK_OFFSET (- (size + sizf));\
2700      CHECK_REF (p, ref_file, M_REF_FILE);\
2701      reset_transput_buffer (UNFORMATTED_BUFFER);\
2702      open_for_writing (p, ref_file);\
2703      genie_write_standard (p, MODE (m), STACK_OFFSET (-size), ref_file);\
2704      write_purge_buffer (p, ref_file, UNFORMATTED_BUFFER);\
2705      DECREMENT_STACK_POINTER (p, size + sizf);\
2706    }\
2707    void genie_read_##n (NODE_T * p)\
2708    {\
2709      BYTE_T *z = STACK_TOP;\
2710      INCREMENT_STACK_POINTER (p, SIZE (MODE (m)));\
2711      ADDR_T pop_sp = A68_SP;\
2712      open_for_reading (p, A68 (stand_in));\
2713      genie_read_standard (p, MODE (m), z, A68 (stand_in));\
2714      A68_SP = pop_sp;\
2715    }\
2716    void genie_print_##n (NODE_T * p)\
2717    {\
2718      int size = SIZE (MODE (m));\
2719      reset_transput_buffer (UNFORMATTED_BUFFER);\
2720      open_for_writing (p, A68 (stand_out));\
2721      genie_write_standard (p, MODE (m), STACK_OFFSET (-size), A68 (stand_out));\
2722      write_purge_buffer (p, A68 (stand_out), UNFORMATTED_BUFFER);\
2723      DECREMENT_STACK_POINTER (p, size);\
2724    }
2725  
2726  A68C_TRANSPUT (int, INT);
2727  A68C_TRANSPUT (long_int, LONG_INT);
2728  A68C_TRANSPUT (long_mp_int, LONG_LONG_INT);
2729  A68C_TRANSPUT (real, REAL);
2730  A68C_TRANSPUT (long_real, LONG_REAL);
2731  A68C_TRANSPUT (long_mp_real, LONG_LONG_REAL);
2732  A68C_TRANSPUT (bits, BITS);
2733  A68C_TRANSPUT (long_bits, LONG_BITS);
2734  A68C_TRANSPUT (long_mp_bits, LONG_LONG_BITS);
2735  A68C_TRANSPUT (bool, BOOL);
2736  A68C_TRANSPUT (char, CHAR);
2737  A68C_TRANSPUT (string, STRING);
2738  
2739  #undef A68C_TRANSPUT
2740  
2741  #define A68C_TRANSPUT(n, s, m)\
2742   void genie_get_##n (NODE_T * p) {\
2743      A68_REF ref_file;\
2744      POP_REF (p, &ref_file);\
2745      CHECK_REF (p, ref_file, M_REF_FILE);\
2746      PUSH_REF (p, ref_file);\
2747      genie_get_##s (p);\
2748      PUSH_REF (p, ref_file);\
2749      genie_get_##s (p);\
2750    }\
2751    void genie_put_##n (NODE_T * p) {\
2752      int size = SIZE (MODE (m)), sizf = SIZE (M_REF_FILE);\
2753      A68_REF ref_file = * (A68_REF *) STACK_OFFSET (- (size + sizf));\
2754      CHECK_REF (p, ref_file, M_REF_FILE);\
2755      reset_transput_buffer (UNFORMATTED_BUFFER);\
2756      open_for_writing (p, ref_file);\
2757      genie_write_standard (p, MODE (m), STACK_OFFSET (-size), ref_file);\
2758      write_purge_buffer (p, ref_file, UNFORMATTED_BUFFER);\
2759      DECREMENT_STACK_POINTER (p, size + sizf);\
2760    }\
2761    void genie_read_##n (NODE_T * p) {\
2762      genie_read_##s (p);\
2763      genie_read_##s (p);\
2764    }\
2765    void genie_print_##n (NODE_T * p) {\
2766      int size = SIZE (MODE (m));\
2767      reset_transput_buffer (UNFORMATTED_BUFFER);\
2768      open_for_writing (p, A68 (stand_out));\
2769      genie_write_standard (p, MODE (m), STACK_OFFSET (-size), A68 (stand_out));\
2770      write_purge_buffer (p, A68 (stand_out), UNFORMATTED_BUFFER);\
2771      DECREMENT_STACK_POINTER (p, size);\
2772    }
2773  
2774  A68C_TRANSPUT (complex, real, COMPLEX);
2775  A68C_TRANSPUT (mp_complex, long_real, LONG_COMPLEX);
2776  A68C_TRANSPUT (long_mp_complex, long_mp_real, LONG_LONG_COMPLEX);
2777  
2778  #undef A68C_TRANSPUT
2779  
2780  //! @brief PROC STRING read line
2781  
2782  void genie_read_line (NODE_T * p)
2783  {
2784  #if defined (HAVE_READLINE)
2785    char *line = readline ("");
2786    if (line != NO_TEXT && (int) strlen (line) > 0) {
2787      add_history (line);
2788    }
2789    PUSH_REF (p, c_to_a_string (p, line, DEFAULT_WIDTH));
2790    a68_free (line);
2791  #else
2792    genie_read_string (p);
2793    genie_stand_in (p);
2794    genie_new_line (p);
2795  #endif
2796  }
     


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