rts-formatted.c

     
   1  //! @file rts-formatted.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  //! Formatted transput.
  25  
  26  #include "a68g.h"
  27  #include "a68g-genie.h"
  28  #include "a68g-frames.h"
  29  #include "a68g-prelude.h"
  30  #include "a68g-mp.h"
  31  #include "a68g-double.h"
  32  #include "a68g-transput.h"
  33  
  34  // Transput - Formatted transput.
  35  // In Algol68G, a value of mode FORMAT looks like a routine text. The value
  36  // comprises a pointer to its environment in the stack, and a pointer where the
  37  // format text is at in the syntax tree.
  38  
  39  #define INT_DIGITS "0123456789"
  40  #define BITS_DIGITS "0123456789abcdefABCDEF"
  41  #define INT_DIGITS_BLANK " 0123456789"
  42  #define BITS_DIGITS_BLANK " 0123456789abcdefABCDEF"
  43  #define SIGN_DIGITS " +-"
  44  
  45  //! @brief Convert to other radix, binary up to hexadecimal.
  46  
  47  BOOL_T convert_radix (NODE_T * p, UNSIGNED_T z, int radix, int width)
  48  {
  49    reset_transput_buffer (EDIT_BUFFER);
  50    if (radix < 2 || radix > 16) {
  51      radix = 16;
  52    }
  53    if (width > 0) {
  54      while (width > 0) {
  55        int digit = (int) (z % (UNSIGNED_T) radix);
  56        plusto_transput_buffer (p, digchar (digit), EDIT_BUFFER);
  57        width--;
  58        z /= (UNSIGNED_T) radix;
  59      }
  60      return z == 0;
  61    } else if (width == 0) {
  62      do {
  63        int digit = (int) (z % (UNSIGNED_T) radix);
  64        plusto_transput_buffer (p, digchar (digit), EDIT_BUFFER);
  65        z /= (UNSIGNED_T) radix;
  66      } while (z > 0);
  67      return A68_TRUE;
  68    } else {
  69      return A68_FALSE;
  70    }
  71  }
  72  
  73  //! @brief Handle format error event.
  74  
  75  void format_error (NODE_T * p, A68_REF ref_file, char *diag)
  76  {
  77    A68_FILE *f = FILE_DEREF (&ref_file);
  78    on_event_handler (p, FORMAT_ERROR_MENDED (f), ref_file);
  79    A68_BOOL z;
  80    POP_OBJECT (p, &z, A68_BOOL);
  81    if (VALUE (&z) == A68_FALSE) {
  82      diagnostic (A68_RUNTIME_ERROR, p, diag);
  83      exit_genie (p, A68_RUNTIME_ERROR);
  84    }
  85  }
  86  
  87  //! @brief Initialise processing of pictures.
  88  
  89  void initialise_collitems (NODE_T * p)
  90  {
  91  // Every picture has a counter that says whether it has not been used OR the number
  92  // of times it can still be used.
  93    for (; p != NO_NODE; FORWARD (p)) {
  94      if (IS (p, PICTURE)) {
  95        A68_COLLITEM *z = (A68_COLLITEM *) FRAME_LOCAL (A68_FP, OFFSET (TAX (p)));
  96        STATUS (z) = INIT_MASK;
  97        COUNT (z) = ITEM_NOT_USED;
  98      }
  99  // Don't dive into f, g, n frames and collections.
 100      if (!(IS (p, ENCLOSED_CLAUSE) || IS (p, COLLECTION))) {
 101        initialise_collitems (SUB (p));
 102      }
 103    }
 104  }
 105  
 106  //! @brief Initialise processing of format text.
 107  
 108  void open_format_frame (NODE_T * p, A68_REF ref_file, A68_FORMAT * fmt, BOOL_T embedded, BOOL_T init)
 109  {
 110  // Open a new frame for the format text and save for return to embedding one.
 111    A68_FILE *file = FILE_DEREF (&ref_file);
 112  // Integrity check.
 113    if ((STATUS (fmt) & SKIP_FORMAT_MASK) || (BODY (fmt) == NO_NODE)) {
 114      format_error (p, ref_file, ERROR_FORMAT_UNDEFINED);
 115    }
 116  // Ok, seems usable.
 117    NODE_T *dollar = SUB (BODY (fmt));
 118    OPEN_PROC_FRAME (dollar, ENVIRON (fmt));
 119    INIT_STATIC_FRAME (dollar);
 120  // Save old format.
 121    A68_FORMAT *save = (A68_FORMAT *) FRAME_LOCAL (A68_FP, OFFSET (TAX (dollar)));
 122    *save = (embedded == EMBEDDED_FORMAT ? FORMAT (file) : nil_format);
 123    FORMAT (file) = *fmt;
 124  // Reset all collitems.
 125    if (init) {
 126      initialise_collitems (dollar);
 127    }
 128  }
 129  
 130  //! @brief Handle end-of-format event.
 131  
 132  int end_of_format (NODE_T * p, A68_REF ref_file)
 133  {
 134  // Format-items return immediately to the embedding format text. The outermost
 135  //format text calls "on format end".
 136    A68_FILE *file = FILE_DEREF (&ref_file);
 137    NODE_T *dollar = SUB (BODY (&FORMAT (file)));
 138    A68_FORMAT *save = (A68_FORMAT *) FRAME_LOCAL (A68_FP, OFFSET (TAX (dollar)));
 139    if (IS_NIL_FORMAT (save)) {
 140  // Not embedded, outermost format: execute event routine.
 141      on_event_handler (p, FORMAT_END_MENDED (FILE_DEREF (&ref_file)), ref_file);
 142      A68_BOOL z;
 143      POP_OBJECT (p, &z, A68_BOOL);
 144      if (VALUE (&z) == A68_FALSE) {
 145  // Restart format.
 146        A68_FP = FRAME_POINTER (file);
 147        A68_SP = STACK_POINTER (file);
 148        open_format_frame (p, ref_file, &FORMAT (file), NOT_EMBEDDED_FORMAT, A68_TRUE);
 149      }
 150      return NOT_EMBEDDED_FORMAT;
 151    } else {
 152  // Embedded format, return to embedding format, cf. RR.
 153      CLOSE_FRAME;
 154      FORMAT (file) = *save;
 155      return EMBEDDED_FORMAT;
 156    }
 157  }
 158  
 159  //! @brief Return integral value of replicator.
 160  
 161  int get_replicator_value (NODE_T * p, BOOL_T check)
 162  {
 163    int z = 0;
 164    if (IS (p, STATIC_REPLICATOR)) {
 165      A68_INT u;
 166      if (genie_string_to_value_internal (p, M_INT, NSYMBOL (p), (BYTE_T *) & u) == A68_FALSE) {
 167        diagnostic (A68_RUNTIME_ERROR, p, ERROR_IN_DENOTATION, M_INT);
 168        exit_genie (p, A68_RUNTIME_ERROR);
 169      }
 170      z = VALUE (&u);
 171    } else if (IS (p, DYNAMIC_REPLICATOR)) {
 172      A68_INT u;
 173      GENIE_UNIT (NEXT_SUB (p));
 174      POP_OBJECT (p, &u, A68_INT);
 175      z = VALUE (&u);
 176    } else if (IS (p, REPLICATOR)) {
 177      z = get_replicator_value (SUB (p), check);
 178    }
 179  // Not conform RR as Andrew Herbert rightfully pointed out.
 180  //  if (check && z < 0) {
 181  //    diagnostic (A68_RUNTIME_ERROR, p, ERROR_FORMAT_INVALID_REPLICATOR);
 182  //    exit_genie (p, A68_RUNTIME_ERROR);
 183  //  }
 184    if (z < 0) {
 185      z = 0;
 186    }
 187    return z;
 188  }
 189  
 190  //! @brief Return first available pattern.
 191  
 192  NODE_T *scan_format_pattern (NODE_T * p, A68_REF ref_file)
 193  {
 194    for (; p != NO_NODE; FORWARD (p)) {
 195      if (IS (p, PICTURE_LIST)) {
 196        NODE_T *prio = scan_format_pattern (SUB (p), ref_file);
 197        if (prio != NO_NODE) {
 198          return prio;
 199        }
 200      }
 201      if (IS (p, PICTURE)) {
 202        NODE_T *picture = SUB (p);
 203        A68_COLLITEM *collitem = (A68_COLLITEM *) FRAME_LOCAL (A68_FP, OFFSET (TAX (p)));
 204        if (COUNT (collitem) != 0) {
 205          if (IS (picture, A68_PATTERN)) {
 206            COUNT (collitem) = 0; // This pattern is now done
 207            picture = SUB (picture);
 208            if (ATTRIBUTE (picture) != FORMAT_PATTERN) {
 209              return picture;
 210            } else {
 211              NODE_T *pat;
 212              A68_FORMAT z;
 213              A68_FILE *file = FILE_DEREF (&ref_file);
 214              GENIE_UNIT (NEXT_SUB (picture));
 215              POP_OBJECT (p, &z, A68_FORMAT);
 216              open_format_frame (p, ref_file, &z, EMBEDDED_FORMAT, A68_TRUE);
 217              pat = scan_format_pattern (SUB (BODY (&FORMAT (file))), ref_file);
 218              if (pat != NO_NODE) {
 219                return pat;
 220              } else {
 221                (void) end_of_format (p, ref_file);
 222              }
 223            }
 224          } else if (IS (picture, INSERTION)) {
 225            A68_FILE *file = FILE_DEREF (&ref_file);
 226            if (READ_MOOD (file)) {
 227              read_insertion (picture, ref_file);
 228            } else if (WRITE_MOOD (file)) {
 229              write_insertion (picture, ref_file, INSERTION_NORMAL);
 230            } else {
 231              ABEND (A68_TRUE, ERROR_INTERNAL_CONSISTENCY, __func__);
 232            }
 233            COUNT (collitem) = 0; // This insertion is now done
 234          } else if (IS (picture, REPLICATOR) || IS (picture, COLLECTION)) {
 235            BOOL_T siga = A68_TRUE;
 236            NODE_T *a68_select = NO_NODE;
 237            if (COUNT (collitem) == ITEM_NOT_USED) {
 238              if (IS (picture, REPLICATOR)) {
 239                COUNT (collitem) = get_replicator_value (SUB (p), A68_TRUE);
 240                siga = (BOOL_T) (COUNT (collitem) > 0);
 241                FORWARD (picture);
 242              } else {
 243                COUNT (collitem) = 1;
 244              }
 245              initialise_collitems (NEXT_SUB (picture));
 246            } else if (IS (picture, REPLICATOR)) {
 247              FORWARD (picture);
 248            }
 249            while (siga) {
 250  // Get format item from collection. If collection is done, but repitition is not,
 251  // then re-initialise the collection and repeat.
 252              a68_select = scan_format_pattern (NEXT_SUB (picture), ref_file);
 253              if (a68_select != NO_NODE) {
 254                return a68_select;
 255              } else {
 256                COUNT (collitem)--;
 257                siga = (BOOL_T) (COUNT (collitem) > 0);
 258                if (siga) {
 259                  initialise_collitems (NEXT_SUB (picture));
 260                }
 261              }
 262            }
 263          }
 264        }
 265      }
 266    }
 267    return NO_NODE;
 268  }
 269  
 270  //! @brief Return first available pattern.
 271  
 272  NODE_T *get_next_format_pattern (NODE_T * p, A68_REF ref_file, BOOL_T mood)
 273  {
 274  // "mood" can be WANT_PATTERN: pattern needed by caller, so perform end-of-format
 275  // if needed or SKIP_PATTERN: just emptying current pattern/collection/format.
 276    A68_FILE *file = FILE_DEREF (&ref_file);
 277    if (BODY (&FORMAT (file)) == NO_NODE) {
 278      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FORMAT_EXHAUSTED);
 279      exit_genie (p, A68_RUNTIME_ERROR);
 280      return NO_NODE;
 281    } else {
 282      NODE_T *pat = scan_format_pattern (SUB (BODY (&FORMAT (file))), ref_file);
 283      if (pat == NO_NODE) {
 284        if (mood == WANT_PATTERN) {
 285          int z;
 286          do {
 287            z = end_of_format (p, ref_file);
 288            pat = scan_format_pattern (SUB (BODY (&FORMAT (file))), ref_file);
 289          } while (z == EMBEDDED_FORMAT && pat == NO_NODE);
 290          if (pat == NO_NODE) {
 291            diagnostic (A68_RUNTIME_ERROR, p, ERROR_FORMAT_EXHAUSTED);
 292            exit_genie (p, A68_RUNTIME_ERROR);
 293          }
 294        }
 295      }
 296      return pat;
 297    }
 298  }
 299  
 300  //! @brief Diagnostic_node in case mode does not match picture.
 301  
 302  void pattern_error (NODE_T * p, MOID_T * mode, int att)
 303  {
 304    diagnostic (A68_RUNTIME_ERROR, p, ERROR_FORMAT_CANNOT_TRANSPUT, mode, att);
 305    exit_genie (p, A68_RUNTIME_ERROR);
 306  }
 307  
 308  //! @brief Unite value at top of stack to NUMBER.
 309  
 310  void unite_to_number (NODE_T * p, MOID_T * mode, BYTE_T * item)
 311  {
 312    ADDR_T pop_sp = A68_SP;
 313    PUSH_UNION (p, mode);
 314    PUSH (p, item, (int) SIZE (mode));
 315    A68_SP = pop_sp + SIZE (M_NUMBER);
 316  }
 317  
 318  //! @brief Write a group of insertions.
 319  
 320  void write_insertion (NODE_T * p, A68_REF ref_file, MOOD_T mood)
 321  {
 322    for (; p != NO_NODE; FORWARD (p)) {
 323      write_insertion (SUB (p), ref_file, mood);
 324      if (IS (p, FORMAT_ITEM_L)) {
 325        plusab_transput_buffer (p, FORMATTED_BUFFER, NEWLINE_CHAR);
 326        write_purge_buffer (p, ref_file, FORMATTED_BUFFER);
 327      } else if (IS (p, FORMAT_ITEM_P)) {
 328        plusab_transput_buffer (p, FORMATTED_BUFFER, FORMFEED_CHAR);
 329        write_purge_buffer (p, ref_file, FORMATTED_BUFFER);
 330      } else if (IS (p, FORMAT_ITEM_X) || IS (p, FORMAT_ITEM_Q)) {
 331        plusab_transput_buffer (p, FORMATTED_BUFFER, BLANK_CHAR);
 332      } else if (IS (p, FORMAT_ITEM_Y)) {
 333        PUSH_REF (p, ref_file);
 334        PUSH_VALUE (p, -1, A68_INT);
 335        genie_set (p);
 336      } else if (IS (p, LITERAL)) {
 337        if (mood & INSERTION_NORMAL) {
 338          add_string_transput_buffer (p, FORMATTED_BUFFER, NSYMBOL (p));
 339        } else if (mood & INSERTION_BLANK) {
 340          int k = (int) strlen (NSYMBOL (p));
 341          for (int j = 1; j <= k; j++) {
 342            plusab_transput_buffer (p, FORMATTED_BUFFER, BLANK_CHAR);
 343          }
 344        }
 345      } else if (IS (p, REPLICATOR)) {
 346        int k = get_replicator_value (SUB (p), A68_TRUE);
 347        if (ATTRIBUTE (SUB_NEXT (p)) != FORMAT_ITEM_K) {
 348          for (int j = 1; j <= k; j++) {
 349            write_insertion (NEXT (p), ref_file, mood);
 350          }
 351        } else {
 352          int pos = get_transput_buffer_index (FORMATTED_BUFFER);
 353          for (int j = 1; j < (k - pos); j++) {
 354            plusab_transput_buffer (p, FORMATTED_BUFFER, BLANK_CHAR);
 355          }
 356        }
 357        return;
 358      }
 359    }
 360  }
 361  
 362  //! @brief Write string to file following current format.
 363  
 364  void write_string_pattern (NODE_T * p, MOID_T * mode, A68_REF ref_file, char **str)
 365  {
 366    for (; p != NO_NODE; FORWARD (p)) {
 367      if (IS (p, INSERTION)) {
 368        write_insertion (SUB (p), ref_file, INSERTION_NORMAL);
 369      } else if (IS (p, FORMAT_ITEM_A)) {
 370        if ((*str)[0] != NULL_CHAR) {
 371          plusab_transput_buffer (p, FORMATTED_BUFFER, (*str)[0]);
 372          (*str)++;
 373        } else {
 374          value_error (p, mode, ref_file);
 375        }
 376      } else if (IS (p, FORMAT_ITEM_S)) {
 377        if ((*str)[0] != NULL_CHAR) {
 378          (*str)++;
 379        } else {
 380          value_error (p, mode, ref_file);
 381        }
 382        return;
 383      } else if (IS (p, REPLICATOR)) {
 384        int k = get_replicator_value (SUB (p), A68_TRUE);
 385        for (int j = 1; j <= k; j++) {
 386          write_string_pattern (NEXT (p), mode, ref_file, str);
 387        }
 388        return;
 389      } else {
 390        write_string_pattern (SUB (p), mode, ref_file, str);
 391      }
 392    }
 393  }
 394  
 395  //! @brief Scan c_pattern.
 396  
 397  void scan_c_pattern (NODE_T * p, BOOL_T * right_align, BOOL_T * sign, int *width, int *after, int *letter)
 398  {
 399    if (IS (p, FORMAT_ITEM_ESCAPE)) {
 400      FORWARD (p);
 401    }
 402    if (IS (p, FORMAT_ITEM_MINUS)) {
 403      *right_align = A68_TRUE;
 404      FORWARD (p);
 405    } else {
 406      *right_align = A68_FALSE;
 407    }
 408    if (IS (p, FORMAT_ITEM_PLUS)) {
 409      *sign = A68_TRUE;
 410      FORWARD (p);
 411    } else {
 412      *sign = A68_FALSE;
 413    }
 414    if (IS (p, REPLICATOR)) {
 415      *width = get_replicator_value (SUB (p), A68_TRUE);
 416      FORWARD (p);
 417    }
 418    if (IS (p, FORMAT_ITEM_POINT)) {
 419      FORWARD (p);
 420    }
 421    if (IS (p, REPLICATOR)) {
 422      *after = get_replicator_value (SUB (p), A68_TRUE);
 423      FORWARD (p);
 424    }
 425    *letter = ATTRIBUTE (p);
 426  }
 427  
 428  //! @brief Write appropriate insertion from a choice pattern.
 429  
 430  void write_choice_pattern (NODE_T * p, A68_REF ref_file, int *count)
 431  {
 432    for (; p != NO_NODE; FORWARD (p)) {
 433      write_choice_pattern (SUB (p), ref_file, count);
 434      if (IS (p, PICTURE)) {
 435        (*count)--;
 436        if (*count == 0) {
 437          write_insertion (SUB (p), ref_file, INSERTION_NORMAL);
 438        }
 439      }
 440    }
 441  }
 442  
 443  //! @brief Write appropriate insertion from a boolean pattern.
 444  
 445  void write_boolean_pattern (NODE_T * p, A68_REF ref_file, BOOL_T z)
 446  {
 447    int k = (z ? 1 : 2);
 448    write_choice_pattern (p, ref_file, &k);
 449  }
 450  
 451  //! @brief Write value according to a general pattern.
 452  
 453  void write_number_generic (NODE_T * p, MOID_T * mode, BYTE_T * item, int mod)
 454  {
 455  // Push arguments.
 456    unite_to_number (p, mode, item);
 457    GENIE_UNIT (NEXT_SUB (p));
 458    A68_REF row;
 459    POP_REF (p, &row);
 460    A68_ARRAY *arr; A68_TUPLE *tup;
 461    GET_DESCRIPTOR (arr, tup, &row);
 462    int size = ROW_SIZE (tup);
 463    if (size > 0) {
 464      BYTE_T *base_address = DEREF (BYTE_T, &ARRAY (arr));
 465      for (int i = LWB (tup); i <= UPB (tup); i++) {
 466        int addr = INDEX_1_DIM (arr, tup, i);
 467        int arg = VALUE ((A68_INT *) & (base_address[addr]));
 468        PUSH_VALUE (p, arg, A68_INT);
 469      }
 470    }
 471  // Make a string.
 472    if (mod == FORMAT_ITEM_G) {
 473      switch (size) {
 474      case 1: {
 475          genie_whole (p);
 476          break;
 477        }
 478      case 2: {
 479          genie_fixed (p);
 480          break;
 481        }
 482      case 3: {
 483          genie_float (p);
 484          break;
 485        }
 486      default: {
 487          diagnostic (A68_RUNTIME_ERROR, p, ERROR_FORMAT_INTS_REQUIRED, M_INT);
 488          exit_genie (p, A68_RUNTIME_ERROR);
 489          break;
 490        }
 491      }
 492    } else if (mod == FORMAT_ITEM_H) {
 493      A68_INT a_width, a_after, a_expo, a_mult;
 494      STATUS (&a_width) = INIT_MASK;
 495      VALUE (&a_width) = 0;
 496      STATUS (&a_after) = INIT_MASK;
 497      VALUE (&a_after) = 0;
 498      STATUS (&a_expo) = INIT_MASK;
 499      VALUE (&a_expo) = 0;
 500      STATUS (&a_mult) = INIT_MASK;
 501      VALUE (&a_mult) = 0;
 502  // Set default values 
 503      int def_expo = 0;
 504      if (mode == M_REAL || mode == M_INT) {
 505        def_expo = A68_EXP_WIDTH + 1;
 506      } else if (mode == M_LONG_REAL || mode == M_LONG_INT) {
 507        def_expo = A68_LONG_EXP_WIDTH + 1;
 508      } else if (mode == M_LONG_LONG_REAL || mode == M_LONG_LONG_INT) {
 509        def_expo = A68_LONG_LONG_EXP_WIDTH + 1;
 510      }
 511      int def_mult = 3;
 512  // Pop user values 
 513      switch (size) {
 514      case 1: {
 515          POP_OBJECT (p, &a_after, A68_INT);
 516          VALUE (&a_width) = VALUE (&a_after) + def_expo + 4;
 517          VALUE (&a_expo) = def_expo;
 518          VALUE (&a_mult) = def_mult;
 519          break;
 520        }
 521      case 2: {
 522          POP_OBJECT (p, &a_mult, A68_INT);
 523          POP_OBJECT (p, &a_after, A68_INT);
 524          VALUE (&a_width) = VALUE (&a_after) + def_expo + 4;
 525          VALUE (&a_expo) = def_expo;
 526          break;
 527        }
 528      case 3: {
 529          POP_OBJECT (p, &a_mult, A68_INT);
 530          POP_OBJECT (p, &a_after, A68_INT);
 531          POP_OBJECT (p, &a_width, A68_INT);
 532          VALUE (&a_expo) = def_expo;
 533          break;
 534        }
 535      case 4: {
 536          POP_OBJECT (p, &a_mult, A68_INT);
 537          POP_OBJECT (p, &a_expo, A68_INT);
 538          POP_OBJECT (p, &a_after, A68_INT);
 539          POP_OBJECT (p, &a_width, A68_INT);
 540          break;
 541        }
 542      default: {
 543          diagnostic (A68_RUNTIME_ERROR, p, ERROR_FORMAT_INTS_REQUIRED, M_INT);
 544          exit_genie (p, A68_RUNTIME_ERROR);
 545          break;
 546        }
 547      }
 548      PUSH_VALUE (p, VALUE (&a_width), A68_INT);
 549      PUSH_VALUE (p, VALUE (&a_after), A68_INT);
 550      PUSH_VALUE (p, VALUE (&a_expo), A68_INT);
 551      PUSH_VALUE (p, VALUE (&a_mult), A68_INT);
 552      genie_real (p);
 553    }
 554    add_string_from_stack_transput_buffer (p, FORMATTED_BUFFER);
 555  }
 556  
 557  //! @brief Write %[-][+][w][.][d]s/d/i/f/e/b/o/x formats.
 558  
 559  void write_c_pattern (NODE_T * p, MOID_T * mode, BYTE_T * item, A68_REF ref_file)
 560  {
 561    ADDR_T pop_sp = A68_SP;
 562    BOOL_T right_align, sign, invalid;
 563    int width = 0, after = 0, letter;
 564    char *str = NO_TEXT;
 565    char tmp[2]; // In same scope as str!
 566    if (IS (p, CHAR_C_PATTERN)) {
 567      A68_CHAR *z = (A68_CHAR *) item;
 568      tmp[0] = (char) VALUE (z);
 569      tmp[1] = NULL_CHAR;
 570      str = (char *) &tmp;
 571      width = (int) strlen (str);
 572      scan_c_pattern (SUB (p), &right_align, &sign, &width, &after, &letter);
 573    } else if (IS (p, STRING_C_PATTERN)) {
 574      str = (char *) item;
 575      width = (int) strlen (str);
 576      scan_c_pattern (SUB (p), &right_align, &sign, &width, &after, &letter);
 577    } else if (IS (p, INTEGRAL_C_PATTERN)) {
 578      width = 0;
 579      scan_c_pattern (SUB (p), &right_align, &sign, &width, &after, &letter);
 580      unite_to_number (p, mode, item);
 581      PUSH_VALUE (p, (sign ? width : -width), A68_INT);
 582      str = whole (p);
 583    } else if (IS (p, FIXED_C_PATTERN) || IS (p, FLOAT_C_PATTERN) || IS (p, GENERAL_C_PATTERN)) {
 584      int att = ATTRIBUTE (p), expval = 0, expo = 0;
 585      if (att == FLOAT_C_PATTERN || att == GENERAL_C_PATTERN) {
 586        int digits = 0;
 587        if (mode == M_REAL || mode == M_INT) {
 588          width = A68_REAL_WIDTH + A68_EXP_WIDTH + 4;
 589          after = A68_REAL_WIDTH - 1;
 590          expo = A68_EXP_WIDTH + 1;
 591        } else if (mode == M_LONG_REAL || mode == M_LONG_INT) {
 592          width = A68_LONG_REAL_WIDTH + A68_LONG_EXP_WIDTH + 4;
 593          after = A68_LONG_REAL_WIDTH - 1;
 594          expo = A68_LONG_EXP_WIDTH + 1;
 595        } else if (mode == M_LONG_LONG_REAL || mode == M_LONG_LONG_INT) {
 596          width = A68_LONG_LONG_REAL_WIDTH + A68_LONG_LONG_EXP_WIDTH + 4;
 597          after = A68_LONG_LONG_REAL_WIDTH - 1;
 598          expo = A68_LONG_LONG_EXP_WIDTH + 1;
 599        }
 600        scan_c_pattern (SUB (p), &right_align, &sign, &digits, &after, &letter);
 601        if (digits == 0 && after > 0) {
 602          width = after + expo + 4;
 603        } else if (digits > 0) {
 604          width = digits;
 605        }
 606        unite_to_number (p, mode, item);
 607        PUSH_VALUE (p, (sign ? width : -width), A68_INT);
 608        PUSH_VALUE (p, after, A68_INT);
 609        PUSH_VALUE (p, expo, A68_INT);
 610        PUSH_VALUE (p, 1, A68_INT);
 611        str = real (p);
 612        A68_SP = pop_sp;
 613      }
 614      if (att == GENERAL_C_PATTERN) {
 615        char *expch = strchr (str, EXPONENT_CHAR);
 616        if (expch != NO_TEXT) {
 617          expval = (int) strtol (&(expch[1]), NO_VAR, 10);
 618        }
 619      }
 620      if ((att == FIXED_C_PATTERN) || (att == GENERAL_C_PATTERN && (expval > -4 && expval <= after))) {
 621        int digits = 0;
 622        if (mode == M_REAL || mode == M_INT) {
 623          width = A68_REAL_WIDTH + 2;
 624          after = A68_REAL_WIDTH - 1;
 625        } else if (mode == M_LONG_REAL || mode == M_LONG_INT) {
 626          width = A68_LONG_REAL_WIDTH + 2;
 627          after = A68_LONG_REAL_WIDTH - 1;
 628        } else if (mode == M_LONG_LONG_REAL || mode == M_LONG_LONG_INT) {
 629          width = A68_LONG_LONG_REAL_WIDTH + 2;
 630          after = A68_LONG_LONG_REAL_WIDTH - 1;
 631        }
 632        scan_c_pattern (SUB (p), &right_align, &sign, &digits, &after, &letter);
 633        if (digits == 0 && after > 0) {
 634          width = after + 2;
 635        } else if (digits > 0) {
 636          width = digits;
 637        }
 638        unite_to_number (p, mode, item);
 639        PUSH_VALUE (p, (sign ? width : -width), A68_INT);
 640        PUSH_VALUE (p, after, A68_INT);
 641        str = fixed (p);
 642        A68_SP = pop_sp;
 643      }
 644    } else if (IS (p, BITS_C_PATTERN)) {
 645      int radix = 10, nibble = 1;
 646      width = 0;
 647      scan_c_pattern (SUB (p), &right_align, &sign, &width, &after, &letter);
 648      if (letter == FORMAT_ITEM_B) {
 649        radix = 2;
 650        nibble = 1;
 651      } else if (letter == FORMAT_ITEM_O) {
 652        radix = 8;
 653        nibble = 3;
 654      } else if (letter == FORMAT_ITEM_X) {
 655        radix = 16;
 656        nibble = 4;
 657      }
 658      if (width == 0) {
 659        if (mode == M_BITS) {
 660          width = (int) ceil ((REAL_T) A68_BITS_WIDTH / (REAL_T) nibble);
 661        } else if (mode == M_LONG_BITS || mode == M_LONG_LONG_BITS) {
 662  #if (A68_LEVEL <= 2)
 663          width = (int) ceil ((REAL_T) get_mp_bits_width (mode) / (REAL_T) nibble);
 664  #else
 665          width = (int) ceil ((REAL_T) A68_LONG_BITS_WIDTH / (REAL_T) nibble);
 666  #endif
 667        }
 668      }
 669      if (mode == M_BITS) {
 670        A68_BITS *z = (A68_BITS *) item;
 671        reset_transput_buffer (EDIT_BUFFER);
 672        if (!convert_radix (p, VALUE (z), radix, width)) {
 673          errno = EDOM;
 674          value_error (p, mode, ref_file);
 675        }
 676        str = get_transput_buffer (EDIT_BUFFER);
 677      } else if (mode == M_LONG_BITS) {
 678  #if (A68_LEVEL >= 3)
 679        A68_LONG_BITS *z = (A68_LONG_BITS *) item;
 680        reset_transput_buffer (EDIT_BUFFER);
 681        if (!convert_radix_double (p, VALUE (z), radix, width)) {
 682          errno = EDOM;
 683          value_error (p, mode, ref_file);
 684        }
 685        str = get_transput_buffer (EDIT_BUFFER);
 686  #else
 687        int digits = DIGITS (mode);
 688        MP_T *u = (MP_T *) item;
 689        MP_T *v = nil_mp (p, digits);
 690        MP_T *w = nil_mp (p, digits);
 691        reset_transput_buffer (EDIT_BUFFER);
 692        if (!convert_radix_mp (p, u, radix, width, mode, v, w)) {
 693          errno = EDOM;
 694          value_error (p, mode, ref_file);
 695        }
 696        str = get_transput_buffer (EDIT_BUFFER);
 697  #endif
 698      } else if (mode == M_LONG_LONG_BITS) {
 699  #if (A68_LEVEL <= 2)
 700        int digits = DIGITS (mode);
 701        MP_T *u = (MP_T *) item;
 702        MP_T *v = nil_mp (p, digits);
 703        MP_T *w = nil_mp (p, digits);
 704        reset_transput_buffer (EDIT_BUFFER);
 705        if (!convert_radix_mp (p, u, radix, width, mode, v, w)) {
 706          errno = EDOM;
 707          value_error (p, mode, ref_file);
 708        }
 709        str = get_transput_buffer (EDIT_BUFFER);
 710  #endif
 711      }
 712    }
 713  // Did the conversion succeed?.
 714    if (IS (p, CHAR_C_PATTERN) || IS (p, STRING_C_PATTERN)) {
 715      invalid = A68_FALSE;
 716    } else {
 717      invalid = (strchr (str, ERROR_CHAR) != NO_TEXT);
 718    }
 719    if (invalid) {
 720      value_error (p, mode, ref_file);
 721      (void) error_chars (get_transput_buffer (FORMATTED_BUFFER), width);
 722    } else {
 723  // Align and output.
 724      if (width == 0) {
 725        add_string_transput_buffer (p, FORMATTED_BUFFER, str);
 726      } else {
 727        if (right_align == A68_TRUE) {
 728          while (str[0] == BLANK_CHAR) {
 729            str++;
 730          }
 731          int blanks = width - (int) strlen (str);
 732          if (blanks >= 0) {
 733            add_string_transput_buffer (p, FORMATTED_BUFFER, str);
 734            while (blanks--) {
 735              plusab_transput_buffer (p, FORMATTED_BUFFER, BLANK_CHAR);
 736            }
 737          } else {
 738            value_error (p, mode, ref_file);
 739            (void) error_chars (get_transput_buffer (FORMATTED_BUFFER), width);
 740          }
 741        } else {
 742          while (str[0] == BLANK_CHAR) {
 743            str++;
 744          }
 745          int blanks = width - (int) strlen (str);
 746          if (blanks >= 0) {
 747            while (blanks--) {
 748              plusab_transput_buffer (p, FORMATTED_BUFFER, BLANK_CHAR);
 749            }
 750            add_string_transput_buffer (p, FORMATTED_BUFFER, str);
 751          } else {
 752            value_error (p, mode, ref_file);
 753            (void) error_chars (get_transput_buffer (FORMATTED_BUFFER), width);
 754          }
 755        }
 756      }
 757    }
 758  }
 759  
 760  //! @brief Read one char from file.
 761  
 762  char read_single_char (NODE_T * p, A68_REF ref_file)
 763  {
 764    A68_FILE *file = FILE_DEREF (&ref_file);
 765    int ch = char_scanner (file);
 766    if (ch == EOF_CHAR) {
 767      end_of_file_error (p, ref_file);
 768    }
 769    return (char) ch;
 770  }
 771  
 772  //! @brief Scan n chars from file to input buffer.
 773  
 774  void scan_n_chars (NODE_T * p, int n, MOID_T * m, A68_REF ref_file)
 775  {
 776    (void) m;
 777    for (int k = 0; k < n; k++) {
 778      int ch = read_single_char (p, ref_file);
 779      plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
 780    }
 781  }
 782  
 783  //! @brief Read %[-][+][w][.][d]s/d/i/f/e/b/o/x formats.
 784  
 785  void read_c_pattern (NODE_T * p, MOID_T * mode, BYTE_T * item, A68_REF ref_file)
 786  {
 787    ADDR_T pop_sp = A68_SP;
 788    BOOL_T right_align, sign;
 789    int width, after, letter;
 790    reset_transput_buffer (INPUT_BUFFER);
 791    if (IS (p, CHAR_C_PATTERN)) {
 792      width = 0;
 793      scan_c_pattern (SUB (p), &right_align, &sign, &width, &after, &letter);
 794      if (width == 0) {
 795        genie_read_standard (p, mode, item, ref_file);
 796      } else {
 797        scan_n_chars (p, width, mode, ref_file);
 798        if (width > 1 && right_align == A68_FALSE) {
 799          for (; width > 1; width--) {
 800            (void) pop_char_transput_buffer (INPUT_BUFFER);
 801          }
 802        }
 803        genie_string_to_value (p, mode, item, ref_file);
 804      }
 805    } else if (IS (p, STRING_C_PATTERN)) {
 806      width = 0;
 807      scan_c_pattern (SUB (p), &right_align, &sign, &width, &after, &letter);
 808      if (width == 0) {
 809        genie_read_standard (p, mode, item, ref_file);
 810      } else {
 811        scan_n_chars (p, width, mode, ref_file);
 812        genie_string_to_value (p, mode, item, ref_file);
 813      }
 814    } else if (IS (p, INTEGRAL_C_PATTERN)) {
 815      if (mode != M_INT && mode != M_LONG_INT && mode != M_LONG_LONG_INT) {
 816        pattern_error (p, mode, ATTRIBUTE (p));
 817      } else {
 818        width = 0;
 819        scan_c_pattern (SUB (p), &right_align, &sign, &width, &after, &letter);
 820        if (width == 0) {
 821          genie_read_standard (p, mode, item, ref_file);
 822        } else {
 823          scan_n_chars (p, (sign != 0) ? width + 1 : width, mode, ref_file);
 824          genie_string_to_value (p, mode, item, ref_file);
 825        }
 826      }
 827    } else if (IS (p, FIXED_C_PATTERN) || IS (p, FLOAT_C_PATTERN) || IS (p, GENERAL_C_PATTERN)) {
 828      if (mode != M_REAL && mode != M_LONG_REAL && mode != M_LONG_LONG_REAL) {
 829        pattern_error (p, mode, ATTRIBUTE (p));
 830      } else {
 831        width = 0;
 832        scan_c_pattern (SUB (p), &right_align, &sign, &width, &after, &letter);
 833        if (width == 0) {
 834          genie_read_standard (p, mode, item, ref_file);
 835        } else {
 836          scan_n_chars (p, (sign != 0) ? width + 1 : width, mode, ref_file);
 837          genie_string_to_value (p, mode, item, ref_file);
 838        }
 839      }
 840    } else if (IS (p, BITS_C_PATTERN)) {
 841      if (mode != M_BITS && mode != M_LONG_BITS && mode != M_LONG_LONG_BITS) {
 842        pattern_error (p, mode, ATTRIBUTE (p));
 843      } else {
 844        int radix = 10;
 845        char *str;
 846        width = 0;
 847        scan_c_pattern (SUB (p), &right_align, &sign, &width, &after, &letter);
 848        if (letter == FORMAT_ITEM_B) {
 849          radix = 2;
 850        } else if (letter == FORMAT_ITEM_O) {
 851          radix = 8;
 852        } else if (letter == FORMAT_ITEM_X) {
 853          radix = 16;
 854        }
 855        str = get_transput_buffer (INPUT_BUFFER);
 856        if (width == 0) {
 857          A68_FILE *file = FILE_DEREF (&ref_file);
 858          int ch;
 859          ASSERT (a68_bufprt (str, (size_t) TRANSPUT_BUFFER_SIZE, "%dr", radix) >= 0);
 860          set_transput_buffer_index (INPUT_BUFFER, (int) strlen (str));
 861          ch = char_scanner (file);
 862          while (ch != EOF_CHAR && (IS_SPACE (ch) || IS_NL_FF (ch))) {
 863            if (IS_NL_FF (ch)) {
 864              skip_nl_ff (p, &ch, ref_file);
 865            } else {
 866              ch = char_scanner (file);
 867            }
 868          }
 869          while (ch != EOF_CHAR && IS_XDIGIT (ch)) {
 870            plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
 871            ch = char_scanner (file);
 872          }
 873          unchar_scanner (p, file, (char) ch);
 874        } else {
 875          ASSERT (a68_bufprt (str, (size_t) TRANSPUT_BUFFER_SIZE, "%dr", radix) >= 0);
 876          set_transput_buffer_index (INPUT_BUFFER, (int) strlen (str));
 877          scan_n_chars (p, width, mode, ref_file);
 878        }
 879        genie_string_to_value (p, mode, item, ref_file);
 880      }
 881    }
 882    A68_SP = pop_sp;
 883  }
 884  
 885  // INTEGRAL, REAL, COMPLEX and BITS patterns.
 886  
 887  //! @brief Count Z and D frames in a mould.
 888  
 889  void count_zd_frames (NODE_T * p, int *z)
 890  {
 891    for (; p != NO_NODE; FORWARD (p)) {
 892      if (IS (p, FORMAT_ITEM_D) || IS (p, FORMAT_ITEM_Z)) {
 893        (*z)++;
 894      } else if (IS (p, REPLICATOR)) {
 895        int k = get_replicator_value (SUB (p), A68_TRUE);
 896        for (int j = 1; j <= k; j++) {
 897          count_zd_frames (NEXT (p), z);
 898        }
 899        return;
 900      } else {
 901        count_zd_frames (SUB (p), z);
 902      }
 903    }
 904  }
 905  
 906  //! @brief Get sign from sign mould.
 907  
 908  NODE_T *get_sign (NODE_T * p)
 909  {
 910    for (; p != NO_NODE; FORWARD (p)) {
 911      NODE_T *q = get_sign (SUB (p));
 912      if (q != NO_NODE) {
 913        return q;
 914      } else if (IS (p, FORMAT_ITEM_PLUS) || IS (p, FORMAT_ITEM_MINUS)) {
 915        return p;
 916      }
 917    }
 918    return NO_NODE;
 919  }
 920  
 921  //! @brief Shift sign through Z frames until non-zero digit or D frame.
 922  
 923  void shift_sign (NODE_T * p, char **q)
 924  {
 925    for (; p != NO_NODE && (*q) != NO_TEXT; FORWARD (p)) {
 926      shift_sign (SUB (p), q);
 927      if (IS (p, FORMAT_ITEM_Z)) {
 928        if (((*q)[0] == '+' || (*q)[0] == '-') && (*q)[1] == '0') {
 929          char ch = (*q)[0];
 930          (*q)[0] = (*q)[1];
 931          (*q)[1] = ch;
 932          (*q)++;
 933        }
 934      } else if (IS (p, FORMAT_ITEM_D)) {
 935        (*q) = NO_TEXT;
 936      } else if (IS (p, REPLICATOR)) {
 937        int k = get_replicator_value (SUB (p), A68_TRUE);
 938        for (int j = 1; j <= k; j++) {
 939          shift_sign (NEXT (p), q);
 940        }
 941        return;
 942      }
 943    }
 944  }
 945  
 946  //! @brief Pad trailing blanks to integral until desired width.
 947  
 948  void put_zeroes_to_integral (NODE_T * p, int n)
 949  {
 950    for (; n > 0; n--) {
 951      plusab_transput_buffer (p, EDIT_BUFFER, '0');
 952    }
 953  }
 954  
 955  //! @brief Pad a sign to integral representation.
 956  
 957  void put_sign_to_integral (NODE_T * p, int sign)
 958  {
 959    NODE_T *sign_node = get_sign (SUB (p));
 960    if (IS (sign_node, FORMAT_ITEM_PLUS)) {
 961      plusab_transput_buffer (p, EDIT_BUFFER, (char) (sign >= 0 ? '+' : '-'));
 962    } else {
 963      plusab_transput_buffer (p, EDIT_BUFFER, (char) (sign >= 0 ? BLANK_CHAR : '-'));
 964    }
 965  }
 966  
 967  //! @brief Write point, exponent or plus-i-times symbol.
 968  
 969  void write_pie_frame (NODE_T * p, A68_REF ref_file, int att, int sym)
 970  {
 971    for (; p != NO_NODE; FORWARD (p)) {
 972      if (IS (p, INSERTION)) {
 973        write_insertion (p, ref_file, INSERTION_NORMAL);
 974      } else if (IS (p, att)) {
 975        write_pie_frame (SUB (p), ref_file, att, sym);
 976        return;
 977      } else if (IS (p, sym)) {
 978        add_string_transput_buffer (p, FORMATTED_BUFFER, NSYMBOL (p));
 979      } else if (IS (p, FORMAT_ITEM_S)) {
 980        return;
 981      }
 982    }
 983  }
 984  
 985  //! @brief Write sign when appropriate.
 986  
 987  void write_mould_put_sign (NODE_T * p, char **q)
 988  {
 989    if ((*q)[0] == '+' || (*q)[0] == '-' || (*q)[0] == BLANK_CHAR) {
 990      plusab_transput_buffer (p, FORMATTED_BUFFER, (*q)[0]);
 991      (*q)++;
 992    }
 993  }
 994  
 995  //! @brief Write character according to a mould.
 996  
 997  void add_char_mould (NODE_T * p, char ch, char **q)
 998  {
 999    if (ch != NULL_CHAR) {
1000      plusab_transput_buffer (p, FORMATTED_BUFFER, ch);
1001      (*q)++;
1002    }
1003  }
1004  
1005  //! @brief Write string according to a mould.
1006  
1007  void write_mould (NODE_T * p, A68_REF ref_file, int type, char **q, MOOD_T * mood)
1008  {
1009    for (; p != NO_NODE; FORWARD (p)) {
1010  // Insertions are inserted straight away. Note that we can suppress them using "mood", which is not standard A68.
1011      if (IS (p, INSERTION)) {
1012        write_insertion (SUB (p), ref_file, *mood);
1013      } else {
1014        write_mould (SUB (p), ref_file, type, q, mood);
1015  // Z frames print blanks until first non-zero digits comes.
1016        if (IS (p, FORMAT_ITEM_Z)) {
1017          write_mould_put_sign (p, q);
1018          if ((*q)[0] == '0') {
1019            if (*mood & DIGIT_BLANK) {
1020              add_char_mould (p, BLANK_CHAR, q);
1021              *mood = (*mood & ~INSERTION_NORMAL) | INSERTION_BLANK;
1022            } else if (*mood & DIGIT_NORMAL) {
1023              add_char_mould (p, '0', q);
1024              *mood = (MOOD_T) (DIGIT_NORMAL | INSERTION_NORMAL);
1025            }
1026          } else {
1027            add_char_mould (p, (*q)[0], q);
1028            *mood = (MOOD_T) (DIGIT_NORMAL | INSERTION_NORMAL);
1029          }
1030        }
1031  // D frames print a digit.
1032        else if (IS (p, FORMAT_ITEM_D)) {
1033          write_mould_put_sign (p, q);
1034          add_char_mould (p, (*q)[0], q);
1035          *mood = (MOOD_T) (DIGIT_NORMAL | INSERTION_NORMAL);
1036        }
1037  // Suppressible frames.
1038        else if (IS (p, FORMAT_ITEM_S)) {
1039  // Suppressible frames are ignored in a sign-mould.
1040          if (type == SIGN_MOULD) {
1041            write_mould (NEXT (p), ref_file, type, q, mood);
1042          } else if (type == INTEGRAL_MOULD) {
1043            if ((*q)[0] != NULL_CHAR) {
1044              (*q)++;
1045            }
1046          }
1047          return;
1048        }
1049  // Replicator.
1050        else if (IS (p, REPLICATOR)) {
1051          int k = get_replicator_value (SUB (p), A68_TRUE);
1052          for (int j = 1; j <= k; j++) {
1053            write_mould (NEXT (p), ref_file, type, q, mood);
1054          }
1055          return;
1056        }
1057      }
1058    }
1059  }
1060  
1061  //! @brief Write INT value using int pattern.
1062  
1063  void write_integral_pattern (NODE_T * p, MOID_T * mode, MOID_T * root, BYTE_T * item, A68_REF ref_file)
1064  {
1065    errno = 0;
1066    if (!(mode == M_INT || mode == M_LONG_INT || mode == M_LONG_LONG_INT)) {
1067      pattern_error (p, root, ATTRIBUTE (p));
1068    } else {
1069      ADDR_T pop_sp = A68_SP;
1070      char *str = "*";
1071      int width = 0, sign = 0;
1072      MOOD_T mood;
1073  // Dive into the pattern if needed.
1074      if (IS (p, INTEGRAL_PATTERN)) {
1075        p = SUB (p);
1076      }
1077  // Find width.
1078      count_zd_frames (p, &width);
1079  // Make string.
1080      reset_transput_buffer (EDIT_BUFFER);
1081      if (mode == M_INT) {
1082        A68_INT *z = (A68_INT *) item;
1083        sign = SIGN (VALUE (z));
1084        str = sub_whole (p, ABS (VALUE (z)), width);
1085      } else if (mode == M_LONG_INT) {
1086  #if (A68_LEVEL >= 3)
1087        A68_LONG_INT *z = (A68_LONG_INT *) item;
1088        DOUBLE_NUM_T w = VALUE (z);
1089        sign = sign_double_int (w);
1090        str = long_sub_whole_double (p, abs_double_int (w), width);
1091  #else
1092        MP_T *z = (MP_T *) item;
1093        sign = MP_SIGN (z);
1094        MP_DIGIT (z, 1) = ABS (MP_DIGIT (z, 1));
1095        str = long_sub_whole (p, z, DIGITS (mode), width);
1096  #endif
1097      } else if (mode == M_LONG_LONG_INT) {
1098        MP_T *z = (MP_T *) item;
1099        sign = MP_SIGN (z);
1100        MP_DIGIT (z, 1) = ABS (MP_DIGIT (z, 1));
1101        str = long_sub_whole (p, z, DIGITS (mode), width);
1102      }
1103  // Edit string and output.
1104      if (strchr (str, ERROR_CHAR) != NO_TEXT) {
1105        value_error (p, root, ref_file);
1106      }
1107      if (IS (p, SIGN_MOULD)) {
1108        put_sign_to_integral (p, sign);
1109      } else if (sign < 0) {
1110        value_sign_error (p, root, ref_file);
1111      }
1112      put_zeroes_to_integral (p, width - (int) strlen (str));
1113      add_string_transput_buffer (p, EDIT_BUFFER, str);
1114      str = get_transput_buffer (EDIT_BUFFER);
1115      mood = (MOOD_T) (DIGIT_BLANK | INSERTION_NORMAL);
1116      if (IS (p, SIGN_MOULD)) {
1117        if (str[0] == '+' || str[0] == '-') {
1118          shift_sign (SUB (p), &str);
1119        }
1120        str = get_transput_buffer (EDIT_BUFFER);
1121        write_mould (SUB (p), ref_file, SIGN_MOULD, &str, &mood);
1122        FORWARD (p);
1123      }
1124      if (IS (p, INTEGRAL_MOULD)) {       // This *should* be the case
1125        write_mould (SUB (p), ref_file, INTEGRAL_MOULD, &str, &mood);
1126      }
1127      A68_SP = pop_sp;
1128    }
1129  }
1130  
1131  //! @brief Write REAL value using real pattern.
1132  
1133  void write_real_pattern (NODE_T * p, MOID_T * mode, MOID_T * root, BYTE_T * item, A68_REF ref_file)
1134  {
1135    errno = 0;
1136    if (!(mode == M_REAL || mode == M_LONG_REAL || mode == M_LONG_LONG_REAL || mode == M_INT || mode == M_LONG_INT || mode == M_LONG_LONG_INT)) {
1137      pattern_error (p, root, ATTRIBUTE (p));
1138    } else {
1139      ADDR_T pop_sp = A68_SP;
1140      int stag_digits = 0, frac_digits = 0, expo_digits = 0;
1141      int mant_length, sign = 0, exp_value;
1142      NODE_T *q, *sign_mould = NO_NODE, *stag_mould = NO_NODE, *point_frame = NO_NODE, *frac_mould = NO_NODE, *e_frame = NO_NODE, *expo_mould = NO_NODE;
1143      char *str = NO_TEXT, *stag_str = NO_TEXT, *frac_str = NO_TEXT;
1144      MOOD_T mood;
1145  // Dive into pattern.
1146      q = ((IS (p, REAL_PATTERN)) ? SUB (p) : p);
1147  // Dissect pattern and establish widths.
1148      if (q != NO_NODE && IS (q, SIGN_MOULD)) {
1149        sign_mould = q;
1150        count_zd_frames (SUB (sign_mould), &stag_digits);
1151        FORWARD (q);
1152      }
1153      if (q != NO_NODE && IS (q, INTEGRAL_MOULD)) {
1154        stag_mould = q;
1155        count_zd_frames (SUB (stag_mould), &stag_digits);
1156        FORWARD (q);
1157      }
1158      if (q != NO_NODE && IS (q, FORMAT_POINT_FRAME)) {
1159        point_frame = q;
1160        FORWARD (q);
1161      }
1162      if (q != NO_NODE && IS (q, INTEGRAL_MOULD)) {
1163        frac_mould = q;
1164        count_zd_frames (SUB (frac_mould), &frac_digits);
1165        FORWARD (q);
1166      }
1167      if (q != NO_NODE && IS (q, EXPONENT_FRAME)) {
1168        e_frame = SUB (q);
1169        expo_mould = NEXT_SUB (q);
1170        q = expo_mould;
1171        if (IS (q, SIGN_MOULD)) {
1172          count_zd_frames (SUB (q), &expo_digits);
1173          FORWARD (q);
1174        }
1175        if (IS (q, INTEGRAL_MOULD)) {
1176          count_zd_frames (SUB (q), &expo_digits);
1177        }
1178      }
1179  // Make string representation.
1180      if (point_frame == NO_NODE) {
1181        mant_length = stag_digits;
1182      } else {
1183        mant_length = 1 + stag_digits + frac_digits;
1184      }
1185      if (mode == M_REAL || mode == M_INT) {
1186        REAL_T x;
1187        if (mode == M_REAL) {
1188          x = VALUE ((A68_REAL *) item);
1189        } else {
1190          x = (REAL_T) VALUE ((A68_INT *) item);
1191        }
1192        CHECK_REAL (p, x);
1193        exp_value = 0;
1194        sign = SIGN (x);
1195        if (sign_mould != NO_NODE) {
1196          put_sign_to_integral (sign_mould, sign);
1197        }
1198        x = ABS (x);
1199        if (expo_mould != NO_NODE) {
1200          standardise (&x, stag_digits, frac_digits, &exp_value);
1201        }
1202        str = sub_fixed (p, x, mant_length, frac_digits);
1203      } else if (mode == M_LONG_REAL || mode == M_LONG_INT) {
1204  #if (A68_LEVEL >= 3)
1205        DOUBLE_NUM_T x = VALUE ((A68_DOUBLE *) item);
1206        if (mode == M_LONG_INT) {
1207          x = double_int_to_double (p, x);
1208        }
1209        CHECK_DOUBLE_REAL (p, x.f);
1210        exp_value = 0;
1211        sign = sign_double (x);
1212        if (sign_mould != NO_NODE) {
1213          put_sign_to_integral (sign_mould, sign);
1214        }
1215        x.f = fabs_double (x.f);
1216        if (expo_mould != NO_NODE) {
1217          standardise_double (&(x.f), stag_digits, frac_digits, &exp_value);
1218        }
1219        str = sub_fixed_double (p, x.f, mant_length, frac_digits, A68_LONG_REAL_WIDTH);
1220  #else
1221        ADDR_T pop_sp2 = A68_SP;
1222        int digits = DIGITS (mode);
1223        MP_T *x = nil_mp (p, digits);
1224        (void) move_mp (x, (MP_T *) item, digits);
1225        exp_value = 0;
1226        sign = SIGN (x[2]);
1227        if (sign_mould != NO_NODE) {
1228          put_sign_to_integral (sign_mould, sign);
1229        }
1230        x[2] = ABS (x[2]);
1231        if (expo_mould != NO_NODE) {
1232          long_standardise (p, x, DIGITS (mode), stag_digits, frac_digits, &exp_value);
1233        }
1234        str = long_sub_fixed (p, x, DIGITS (mode), mant_length, frac_digits);
1235        A68_SP = pop_sp2;
1236  #endif
1237      } else if (mode == M_LONG_LONG_REAL || mode == M_LONG_LONG_INT) {
1238        ADDR_T pop_sp2 = A68_SP;
1239        int digits = DIGITS (mode);
1240        MP_T *x = nil_mp (p, digits);
1241        (void) move_mp (x, (MP_T *) item, digits);
1242        exp_value = 0;
1243        sign = SIGN (x[2]);
1244        if (sign_mould != NO_NODE) {
1245          put_sign_to_integral (sign_mould, sign);
1246        }
1247        x[2] = ABS (x[2]);
1248        if (expo_mould != NO_NODE) {
1249          long_standardise (p, x, DIGITS (mode), stag_digits, frac_digits, &exp_value);
1250        }
1251        str = long_sub_fixed (p, x, DIGITS (mode), mant_length, frac_digits);
1252        A68_SP = pop_sp2;
1253      }
1254  // Edit and output the string.
1255      if (strchr (str, ERROR_CHAR) != NO_TEXT) {
1256        value_error (p, root, ref_file);
1257      }
1258      reset_transput_buffer (STRING_BUFFER);
1259      add_string_transput_buffer (p, STRING_BUFFER, str);
1260      stag_str = get_transput_buffer (STRING_BUFFER);
1261      if (strchr (stag_str, ERROR_CHAR) != NO_TEXT) {
1262        value_error (p, root, ref_file);
1263      }
1264      str = strchr (stag_str, POINT_CHAR);
1265      if (str != NO_TEXT) {
1266        frac_str = &str[1];
1267        str[0] = NULL_CHAR;
1268      } else {
1269        frac_str = NO_TEXT;
1270      }
1271  // Stagnant part.
1272      reset_transput_buffer (EDIT_BUFFER);
1273      if (sign_mould != NO_NODE) {
1274        put_sign_to_integral (sign_mould, sign);
1275      } else if (sign < 0) {
1276        value_sign_error (sign_mould, root, ref_file);
1277      }
1278      put_zeroes_to_integral (p, stag_digits - (int) strlen (stag_str));
1279      add_string_transput_buffer (p, EDIT_BUFFER, stag_str);
1280      stag_str = get_transput_buffer (EDIT_BUFFER);
1281      mood = (MOOD_T) (DIGIT_BLANK | INSERTION_NORMAL);
1282      if (sign_mould != NO_NODE) {
1283        if (stag_str[0] == '+' || stag_str[0] == '-') {
1284          shift_sign (SUB (p), &stag_str);
1285        }
1286        stag_str = get_transput_buffer (EDIT_BUFFER);
1287        write_mould (SUB (sign_mould), ref_file, SIGN_MOULD, &stag_str, &mood);
1288      }
1289      if (stag_mould != NO_NODE) {
1290        write_mould (SUB (stag_mould), ref_file, INTEGRAL_MOULD, &stag_str, &mood);
1291      }
1292  // Point frame.
1293      if (point_frame != NO_NODE) {
1294        write_pie_frame (point_frame, ref_file, FORMAT_POINT_FRAME, FORMAT_ITEM_POINT);
1295      }
1296  // Fraction.
1297      if (frac_mould != NO_NODE) {
1298        reset_transput_buffer (EDIT_BUFFER);
1299        add_string_transput_buffer (p, EDIT_BUFFER, frac_str);
1300        frac_str = get_transput_buffer (EDIT_BUFFER);
1301        mood = (MOOD_T) (DIGIT_NORMAL | INSERTION_NORMAL);
1302        write_mould (SUB (frac_mould), ref_file, INTEGRAL_MOULD, &frac_str, &mood);
1303      }
1304  // Exponent.
1305      if (expo_mould != NO_NODE) {
1306        A68_INT z;
1307        STATUS (&z) = INIT_MASK;
1308        VALUE (&z) = exp_value;
1309        if (e_frame != NO_NODE) {
1310          write_pie_frame (e_frame, ref_file, FORMAT_E_FRAME, FORMAT_ITEM_E);
1311        }
1312        write_integral_pattern (expo_mould, M_INT, root, (BYTE_T *) & z, ref_file);
1313      }
1314      A68_SP = pop_sp;
1315    }
1316  }
1317  
1318  //! @brief Write COMPLEX value using complex pattern.
1319  
1320  void write_complex_pattern (NODE_T * p, MOID_T * comp, MOID_T * root, BYTE_T * re, BYTE_T * im, A68_REF ref_file)
1321  {
1322    errno = 0;
1323  // Dissect pattern.
1324    NODE_T *reel = SUB (p);
1325    NODE_T *plus_i_times = NEXT (reel);
1326    NODE_T *imag = NEXT (plus_i_times);
1327  // Write pattern.
1328    write_real_pattern (reel, comp, root, re, ref_file);
1329    write_pie_frame (plus_i_times, ref_file, FORMAT_I_FRAME, FORMAT_ITEM_I);
1330    write_real_pattern (imag, comp, root, im, ref_file);
1331  }
1332  
1333  //! @brief Write BITS value using bits pattern.
1334  
1335  void write_bits_pattern (NODE_T * p, MOID_T * mode, BYTE_T * item, A68_REF ref_file)
1336  {
1337    ADDR_T pop_sp = A68_SP;
1338    int width = 0, radix;
1339    char *str;
1340    if (mode == M_BITS) {
1341      A68_BITS *z = (A68_BITS *) item;
1342  // Establish width and radix.
1343      count_zd_frames (SUB (p), &width);
1344      radix = get_replicator_value (SUB_SUB (p), A68_TRUE);
1345      if (radix < 2 || radix > 16) {
1346        diagnostic (A68_RUNTIME_ERROR, p, ERROR_INVALID_RADIX, radix);
1347        exit_genie (p, A68_RUNTIME_ERROR);
1348      }
1349  // Generate string of correct width.
1350      reset_transput_buffer (EDIT_BUFFER);
1351      if (!convert_radix (p, VALUE (z), radix, width)) {
1352        errno = EDOM;
1353        value_error (p, mode, ref_file);
1354      }
1355    } else if (mode == M_LONG_BITS) {
1356  #if (A68_LEVEL >= 3)
1357      A68_LONG_BITS *z = (A68_LONG_BITS *) item;
1358  // Establish width and radix.
1359      count_zd_frames (SUB (p), &width);
1360      radix = get_replicator_value (SUB_SUB (p), A68_TRUE);
1361      if (radix < 2 || radix > 16) {
1362        diagnostic (A68_RUNTIME_ERROR, p, ERROR_INVALID_RADIX, radix);
1363        exit_genie (p, A68_RUNTIME_ERROR);
1364      }
1365  // Generate string of correct width.
1366      reset_transput_buffer (EDIT_BUFFER);
1367      if (!convert_radix_double (p, VALUE (z), radix, width)) {
1368        errno = EDOM;
1369        value_error (p, mode, ref_file);
1370      }
1371  #else
1372      int digits = DIGITS (mode);
1373      MP_T *u = (MP_T *) item;
1374      MP_T *v = nil_mp (p, digits);
1375      MP_T *w = nil_mp (p, digits);
1376  // Establish width and radix.
1377      count_zd_frames (SUB (p), &width);
1378      radix = get_replicator_value (SUB_SUB (p), A68_TRUE);
1379      if (radix < 2 || radix > 16) {
1380        diagnostic (A68_RUNTIME_ERROR, p, ERROR_INVALID_RADIX, radix);
1381        exit_genie (p, A68_RUNTIME_ERROR);
1382      }
1383  // Generate string of correct width.
1384      reset_transput_buffer (EDIT_BUFFER);
1385      if (!convert_radix_mp (p, u, radix, width, mode, v, w)) {
1386        errno = EDOM;
1387        value_error (p, mode, ref_file);
1388      }
1389  #endif
1390    } else if (mode == M_LONG_LONG_BITS) {
1391  #if (A68_LEVEL <= 2)
1392      int digits = DIGITS (mode);
1393      MP_T *u = (MP_T *) item;
1394      MP_T *v = nil_mp (p, digits);
1395      MP_T *w = nil_mp (p, digits);
1396  // Establish width and radix.
1397      count_zd_frames (SUB (p), &width);
1398      radix = get_replicator_value (SUB_SUB (p), A68_TRUE);
1399      if (radix < 2 || radix > 16) {
1400        diagnostic (A68_RUNTIME_ERROR, p, ERROR_INVALID_RADIX, radix);
1401        exit_genie (p, A68_RUNTIME_ERROR);
1402      }
1403  // Generate string of correct width.
1404      reset_transput_buffer (EDIT_BUFFER);
1405      if (!convert_radix_mp (p, u, radix, width, mode, v, w)) {
1406        errno = EDOM;
1407        value_error (p, mode, ref_file);
1408      }
1409  #endif
1410    }
1411  // Output the edited string.
1412    MOOD_T mood = (MOOD_T) (DIGIT_BLANK | INSERTION_NORMAL);
1413    str = get_transput_buffer (EDIT_BUFFER);
1414    write_mould (NEXT_SUB (p), ref_file, INTEGRAL_MOULD, &str, &mood);
1415    A68_SP = pop_sp;
1416  }
1417  
1418  //! @brief Write value to file.
1419  
1420  void genie_write_real_format (NODE_T * p, BYTE_T * item, A68_REF ref_file)
1421  {
1422    if (IS (p, GENERAL_PATTERN) && NEXT_SUB (p) == NO_NODE) {
1423      genie_value_to_string (p, M_REAL, item, ATTRIBUTE (SUB (p)));
1424      add_string_from_stack_transput_buffer (p, FORMATTED_BUFFER);
1425    } else if (IS (p, GENERAL_PATTERN) && NEXT_SUB (p) != NO_NODE) {
1426      write_number_generic (p, M_REAL, item, ATTRIBUTE (SUB (p)));
1427    } else if (IS (p, FIXED_C_PATTERN) || IS (p, FLOAT_C_PATTERN) || IS (p, GENERAL_C_PATTERN)) {
1428      write_c_pattern (p, M_REAL, item, ref_file);
1429    } else if (IS (p, REAL_PATTERN)) {
1430      write_real_pattern (p, M_REAL, M_REAL, item, ref_file);
1431    } else if (IS (p, COMPLEX_PATTERN)) {
1432      A68_REAL im;
1433      STATUS (&im) = INIT_MASK;
1434      VALUE (&im) = 0.0;
1435      write_complex_pattern (p, M_REAL, M_COMPLEX, (BYTE_T *) item, (BYTE_T *) & im, ref_file);
1436    } else {
1437      pattern_error (p, M_REAL, ATTRIBUTE (p));
1438    }
1439  }
1440  
1441  //! @brief Write value to file.
1442  
1443  void genie_write_long_real_format (NODE_T * p, BYTE_T * item, A68_REF ref_file)
1444  {
1445    if (IS (p, GENERAL_PATTERN) && NEXT_SUB (p) == NO_NODE) {
1446      genie_value_to_string (p, M_LONG_REAL, item, ATTRIBUTE (SUB (p)));
1447      add_string_from_stack_transput_buffer (p, FORMATTED_BUFFER);
1448    } else if (IS (p, GENERAL_PATTERN) && NEXT_SUB (p) != NO_NODE) {
1449      write_number_generic (p, M_LONG_REAL, item, ATTRIBUTE (SUB (p)));
1450    } else if (IS (p, FIXED_C_PATTERN) || IS (p, FLOAT_C_PATTERN) || IS (p, GENERAL_C_PATTERN)) {
1451      write_c_pattern (p, M_LONG_REAL, item, ref_file);
1452    } else if (IS (p, REAL_PATTERN)) {
1453      write_real_pattern (p, M_LONG_REAL, M_LONG_REAL, item, ref_file);
1454    } else if (IS (p, COMPLEX_PATTERN)) {
1455  #if (A68_LEVEL >= 3)
1456      ADDR_T pop_sp = A68_SP;
1457      A68_LONG_REAL *z = (A68_LONG_REAL *) STACK_TOP;
1458      DOUBLE_NUM_T im;
1459      im.f = 0.0q;
1460      PUSH_VALUE (p, im, A68_LONG_REAL);
1461      write_complex_pattern (p, M_LONG_REAL, M_LONG_COMPLEX, item, (BYTE_T *) z, ref_file);
1462      A68_SP = pop_sp;
1463  #else
1464      ADDR_T pop_sp = A68_SP;
1465      MP_T *z = nil_mp (p, DIGITS (M_LONG_REAL));
1466      z[0] = (MP_T) INIT_MASK;
1467      write_complex_pattern (p, M_LONG_REAL, M_LONG_COMPLEX, item, (BYTE_T *) z, ref_file);
1468      A68_SP = pop_sp;
1469  #endif
1470    } else {
1471      pattern_error (p, M_LONG_REAL, ATTRIBUTE (p));
1472    }
1473  }
1474  
1475  //! @brief Write value to file.
1476  
1477  void genie_write_long_mp_real_format (NODE_T * p, BYTE_T * item, A68_REF ref_file)
1478  {
1479    if (IS (p, GENERAL_PATTERN) && NEXT_SUB (p) == NO_NODE) {
1480      genie_value_to_string (p, M_LONG_LONG_REAL, item, ATTRIBUTE (SUB (p)));
1481      add_string_from_stack_transput_buffer (p, FORMATTED_BUFFER);
1482    } else if (IS (p, GENERAL_PATTERN) && NEXT_SUB (p) != NO_NODE) {
1483      write_number_generic (p, M_LONG_LONG_REAL, item, ATTRIBUTE (SUB (p)));
1484    } else if (IS (p, FIXED_C_PATTERN) || IS (p, FLOAT_C_PATTERN) || IS (p, GENERAL_C_PATTERN)) {
1485      write_c_pattern (p, M_LONG_LONG_REAL, item, ref_file);
1486    } else if (IS (p, REAL_PATTERN)) {
1487      write_real_pattern (p, M_LONG_LONG_REAL, M_LONG_LONG_REAL, item, ref_file);
1488    } else if (IS (p, COMPLEX_PATTERN)) {
1489      ADDR_T pop_sp = A68_SP;
1490      MP_T *z = nil_mp (p, DIGITS (M_LONG_LONG_REAL));
1491      z[0] = (MP_T) INIT_MASK;
1492      write_complex_pattern (p, M_LONG_LONG_REAL, M_LONG_LONG_COMPLEX, item, (BYTE_T *) z, ref_file);
1493      A68_SP = pop_sp;
1494    } else {
1495      pattern_error (p, M_LONG_LONG_REAL, ATTRIBUTE (p));
1496    }
1497  }
1498  
1499  //! @brief At end of write purge all insertions.
1500  
1501  void purge_format_write (NODE_T * p, A68_REF ref_file)
1502  {
1503  // Problem here is shutting down embedded formats.
1504    BOOL_T siga;
1505    do {
1506      A68_FILE *file;
1507      NODE_T *dollar, *pat;
1508      A68_FORMAT *old_fmt;
1509      while ((pat = get_next_format_pattern (p, ref_file, SKIP_PATTERN)) != NO_NODE) {
1510        format_error (p, ref_file, ERROR_FORMAT_PICTURES);
1511      }
1512      file = FILE_DEREF (&ref_file);
1513      dollar = SUB (BODY (&FORMAT (file)));
1514      old_fmt = (A68_FORMAT *) FRAME_LOCAL (A68_FP, OFFSET (TAX (dollar)));
1515      siga = (BOOL_T) ! IS_NIL_FORMAT (old_fmt);
1516      if (siga) {
1517  // Pop embedded format and proceed.
1518        (void) end_of_format (p, ref_file);
1519      }
1520    } while (siga);
1521  }
1522  
1523  //! @brief Write value to file.
1524  
1525  void genie_write_standard_format (NODE_T * p, MOID_T * mode, BYTE_T * item, A68_REF ref_file, int *formats)
1526  {
1527    errno = 0;
1528    ABEND (mode == NO_MOID, ERROR_INTERNAL_CONSISTENCY, __func__);
1529    if (mode == M_FORMAT) {
1530      A68_FILE *file;
1531      CHECK_REF (p, ref_file, M_REF_FILE);
1532      file = FILE_DEREF (&ref_file);
1533  // Forget about eventual active formats and set up new one.
1534      if (*formats > 0) {
1535        purge_format_write (p, ref_file);
1536      }
1537      (*formats)++;
1538      A68_FP = FRAME_POINTER (file);
1539      A68_SP = STACK_POINTER (file);
1540      open_format_frame (p, ref_file, (A68_FORMAT *) item, NOT_EMBEDDED_FORMAT, A68_TRUE);
1541    } else if (mode == M_PROC_REF_FILE_VOID) {
1542      diagnostic (A68_RUNTIME_ERROR, p, ERROR_UNDEFINED_TRANSPUT, M_PROC_REF_FILE_VOID);
1543      exit_genie (p, A68_RUNTIME_ERROR);
1544    } else if (mode == M_SOUND) {
1545      diagnostic (A68_RUNTIME_ERROR, p, ERROR_UNDEFINED_TRANSPUT, M_SOUND);
1546      exit_genie (p, A68_RUNTIME_ERROR);
1547    } else if (mode == M_INT) {
1548      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
1549      if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) == NO_NODE) {
1550        genie_value_to_string (p, mode, item, ATTRIBUTE (SUB (pat)));
1551        add_string_from_stack_transput_buffer (p, FORMATTED_BUFFER);
1552      } else if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) != NO_NODE) {
1553        write_number_generic (pat, M_INT, item, ATTRIBUTE (SUB (pat)));
1554      } else if (IS (pat, INTEGRAL_C_PATTERN) || IS (pat, FIXED_C_PATTERN) || IS (pat, FLOAT_C_PATTERN) || IS (pat, GENERAL_C_PATTERN)) {
1555        write_c_pattern (pat, M_INT, item, ref_file);
1556      } else if (IS (pat, INTEGRAL_PATTERN)) {
1557        write_integral_pattern (pat, M_INT, M_INT, item, ref_file);
1558      } else if (IS (pat, REAL_PATTERN)) {
1559        write_real_pattern (pat, M_INT, M_INT, item, ref_file);
1560      } else if (IS (pat, COMPLEX_PATTERN)) {
1561        A68_REAL re, im;
1562        STATUS (&re) = INIT_MASK;
1563        VALUE (&re) = (REAL_T) VALUE ((A68_INT *) item);
1564        STATUS (&im) = INIT_MASK;
1565        VALUE (&im) = 0.0;
1566        write_complex_pattern (pat, M_REAL, M_COMPLEX, (BYTE_T *) & re, (BYTE_T *) & im, ref_file);
1567      } else if (IS (pat, CHOICE_PATTERN)) {
1568        int k = VALUE ((A68_INT *) item);
1569        write_choice_pattern (NEXT_SUB (pat), ref_file, &k);
1570      } else {
1571        pattern_error (p, mode, ATTRIBUTE (pat));
1572      }
1573    } else if (mode == M_LONG_INT) {
1574      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
1575      if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) == NO_NODE) {
1576        genie_value_to_string (p, mode, item, ATTRIBUTE (SUB (pat)));
1577        add_string_from_stack_transput_buffer (p, FORMATTED_BUFFER);
1578      } else if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) != NO_NODE) {
1579        write_number_generic (pat, M_LONG_INT, item, ATTRIBUTE (SUB (pat)));
1580      } else if (IS (pat, INTEGRAL_C_PATTERN) || IS (pat, FIXED_C_PATTERN) || IS (pat, FLOAT_C_PATTERN) || IS (pat, GENERAL_C_PATTERN)) {
1581        write_c_pattern (pat, M_LONG_INT, item, ref_file);
1582      } else if (IS (pat, INTEGRAL_PATTERN)) {
1583        write_integral_pattern (pat, M_LONG_INT, M_LONG_INT, item, ref_file);
1584      } else if (IS (pat, REAL_PATTERN)) {
1585        write_real_pattern (pat, M_LONG_INT, M_LONG_INT, item, ref_file);
1586      } else if (IS (pat, COMPLEX_PATTERN)) {
1587  #if (A68_LEVEL >= 3)
1588        ADDR_T pop_sp = A68_SP;
1589        A68_LONG_REAL *z = (A68_LONG_REAL *) STACK_TOP;
1590        DOUBLE_NUM_T im;
1591        im.f = 0.0q;
1592        PUSH_VALUE (p, im, A68_LONG_REAL);
1593        write_complex_pattern (p, M_LONG_REAL, M_LONG_COMPLEX, item, (BYTE_T *) z, ref_file);
1594        A68_SP = pop_sp;
1595  #else
1596        ADDR_T pop_sp = A68_SP;
1597        MP_T *z = nil_mp (p, DIGITS (mode));
1598        z[0] = (MP_T) INIT_MASK;
1599        write_complex_pattern (pat, M_LONG_REAL, M_LONG_COMPLEX, item, (BYTE_T *) z, ref_file);
1600        A68_SP = pop_sp;
1601  #endif
1602      } else if (IS (pat, CHOICE_PATTERN)) {
1603        INT_T k = mp_to_int (p, (MP_T *) item, DIGITS (mode));
1604        int sk;
1605        CHECK_INT_SHORTEN (p, k);
1606        sk = (int) k;
1607        write_choice_pattern (NEXT_SUB (pat), ref_file, &sk);
1608      } else {
1609        pattern_error (p, mode, ATTRIBUTE (pat));
1610      }
1611    } else if (mode == M_LONG_LONG_INT) {
1612      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
1613      if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) == NO_NODE) {
1614        genie_value_to_string (p, mode, item, ATTRIBUTE (SUB (pat)));
1615        add_string_from_stack_transput_buffer (p, FORMATTED_BUFFER);
1616      } else if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) != NO_NODE) {
1617        write_number_generic (pat, M_LONG_LONG_INT, item, ATTRIBUTE (SUB (pat)));
1618      } else if (IS (pat, INTEGRAL_C_PATTERN) || IS (pat, FIXED_C_PATTERN) || IS (pat, FLOAT_C_PATTERN) || IS (pat, GENERAL_C_PATTERN)) {
1619        write_c_pattern (pat, M_LONG_LONG_INT, item, ref_file);
1620      } else if (IS (pat, INTEGRAL_PATTERN)) {
1621        write_integral_pattern (pat, M_LONG_LONG_INT, M_LONG_LONG_INT, item, ref_file);
1622      } else if (IS (pat, REAL_PATTERN)) {
1623        write_real_pattern (pat, M_INT, M_INT, item, ref_file);
1624      } else if (IS (pat, REAL_PATTERN)) {
1625        write_real_pattern (pat, M_LONG_LONG_INT, M_LONG_LONG_INT, item, ref_file);
1626      } else if (IS (pat, COMPLEX_PATTERN)) {
1627        ADDR_T pop_sp = A68_SP;
1628        MP_T *z = nil_mp (p, DIGITS (M_LONG_LONG_REAL));
1629        z[0] = (MP_T) INIT_MASK;
1630        write_complex_pattern (pat, M_LONG_LONG_REAL, M_LONG_LONG_COMPLEX, item, (BYTE_T *) z, ref_file);
1631        A68_SP = pop_sp;
1632      } else if (IS (pat, CHOICE_PATTERN)) {
1633        INT_T k = mp_to_int (p, (MP_T *) item, DIGITS (mode));
1634        int sk;
1635        CHECK_INT_SHORTEN (p, k);
1636        sk = (int) k;
1637        write_choice_pattern (NEXT_SUB (pat), ref_file, &sk);
1638      } else {
1639        pattern_error (p, mode, ATTRIBUTE (pat));
1640      }
1641    } else if (mode == M_REAL) {
1642      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
1643      genie_write_real_format (pat, item, ref_file);
1644    } else if (mode == M_LONG_REAL) {
1645      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
1646      genie_write_long_real_format (pat, item, ref_file);
1647    } else if (mode == M_LONG_LONG_REAL) {
1648      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
1649      genie_write_long_mp_real_format (pat, item, ref_file);
1650    } else if (mode == M_COMPLEX) {
1651      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
1652      if (IS (pat, COMPLEX_PATTERN)) {
1653        write_complex_pattern (pat, M_REAL, M_COMPLEX, &item[0], &item[SIZE (M_REAL)], ref_file);
1654      } else {
1655  // Try writing as two REAL values.
1656        genie_write_real_format (pat, item, ref_file);
1657        genie_write_standard_format (p, M_REAL, &item[SIZE (M_REAL)], ref_file, formats);
1658      }
1659    } else if (mode == M_LONG_COMPLEX) {
1660      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
1661      if (IS (pat, COMPLEX_PATTERN)) {
1662        write_complex_pattern (pat, M_LONG_REAL, M_LONG_COMPLEX, &item[0], &item[SIZE (M_LONG_REAL)], ref_file);
1663      } else {
1664  // Try writing as two LONG REAL values.
1665        genie_write_long_real_format (pat, item, ref_file);
1666        genie_write_standard_format (p, M_LONG_REAL, &item[SIZE (M_LONG_REAL)], ref_file, formats);
1667      }
1668    } else if (mode == M_LONG_LONG_COMPLEX) {
1669      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
1670      if (IS (pat, COMPLEX_PATTERN)) {
1671        write_complex_pattern (pat, M_LONG_LONG_REAL, M_LONG_LONG_COMPLEX, &item[0], &item[SIZE (M_LONG_LONG_REAL)], ref_file);
1672      } else {
1673  // Try writing as two LONG LONG REAL values.
1674        genie_write_long_mp_real_format (pat, item, ref_file);
1675        genie_write_standard_format (p, M_LONG_LONG_REAL, &item[SIZE (M_LONG_LONG_REAL)], ref_file, formats);
1676      }
1677    } else if (mode == M_BOOL) {
1678      A68_BOOL *z = (A68_BOOL *) item;
1679      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
1680      if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) == NO_NODE) {
1681        plusab_transput_buffer (p, FORMATTED_BUFFER, (char) (VALUE (z) == A68_TRUE ? FLIP_CHAR : FLOP_CHAR));
1682      } else if (IS (pat, BOOLEAN_PATTERN)) {
1683        if (NEXT_SUB (pat) == NO_NODE) {
1684          plusab_transput_buffer (p, FORMATTED_BUFFER, (char) (VALUE (z) == A68_TRUE ? FLIP_CHAR : FLOP_CHAR));
1685        } else {
1686          write_boolean_pattern (pat, ref_file, (BOOL_T) (VALUE (z) == A68_TRUE));
1687        }
1688      } else {
1689        pattern_error (p, mode, ATTRIBUTE (pat));
1690      }
1691    } else if (mode == M_BITS) {
1692      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
1693      if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) == NO_NODE) {
1694        char *str = (char *) STACK_TOP;
1695        genie_value_to_string (p, mode, item, ATTRIBUTE (SUB (p)));
1696        add_string_transput_buffer (p, FORMATTED_BUFFER, str);
1697      } else if (IS (pat, BITS_PATTERN)) {
1698        write_bits_pattern (pat, M_BITS, item, ref_file);
1699      } else if (IS (pat, BITS_C_PATTERN)) {
1700        write_c_pattern (pat, M_BITS, item, ref_file);
1701      } else {
1702        pattern_error (p, mode, ATTRIBUTE (pat));
1703      }
1704    } else if (mode == M_LONG_BITS || mode == M_LONG_LONG_BITS) {
1705      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
1706      if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) == NO_NODE) {
1707        char *str = (char *) STACK_TOP;
1708        genie_value_to_string (p, mode, item, ATTRIBUTE (SUB (p)));
1709        add_string_transput_buffer (p, FORMATTED_BUFFER, str);
1710      } else if (IS (pat, BITS_PATTERN)) {
1711        write_bits_pattern (pat, mode, item, ref_file);
1712      } else if (IS (pat, BITS_C_PATTERN)) {
1713        write_c_pattern (pat, mode, item, ref_file);
1714      } else {
1715        pattern_error (p, mode, ATTRIBUTE (pat));
1716      }
1717    } else if (mode == M_CHAR) {
1718      A68_CHAR *z = (A68_CHAR *) item;
1719      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
1720      if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) == NO_NODE) {
1721        plusab_transput_buffer (p, FORMATTED_BUFFER, (char) VALUE (z));
1722      } else if (IS (pat, STRING_PATTERN)) {
1723        char *q = get_transput_buffer (EDIT_BUFFER);
1724        reset_transput_buffer (EDIT_BUFFER);
1725        plusab_transput_buffer (p, EDIT_BUFFER, (char) VALUE (z));
1726        write_string_pattern (pat, mode, ref_file, &q);
1727        if (q[0] != NULL_CHAR) {
1728          value_error (p, mode, ref_file);
1729        }
1730      } else if (IS (pat, STRING_C_PATTERN)) {
1731        char zz[2];
1732        zz[0] = VALUE (z);
1733        zz[1] = '\0';
1734        (void) c_to_a_string (pat, zz, 1);
1735        write_c_pattern (pat, mode, (BYTE_T *) zz, ref_file);
1736      } else {
1737        pattern_error (p, mode, ATTRIBUTE (pat));
1738      }
1739    } else if (mode == M_ROW_CHAR || mode == M_STRING) {
1740  // Handle these separately instead of printing [] CHAR.
1741      A68_REF row = *(A68_REF *) item;
1742      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
1743      if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) == NO_NODE) {
1744        PUSH_REF (p, row);
1745        add_string_from_stack_transput_buffer (p, FORMATTED_BUFFER);
1746      } else if (IS (pat, STRING_PATTERN)) {
1747        char *q;
1748        PUSH_REF (p, row);
1749        reset_transput_buffer (EDIT_BUFFER);
1750        add_string_from_stack_transput_buffer (p, EDIT_BUFFER);
1751        q = get_transput_buffer (EDIT_BUFFER);
1752        write_string_pattern (pat, mode, ref_file, &q);
1753        if (q[0] != NULL_CHAR) {
1754          value_error (p, mode, ref_file);
1755        }
1756      } else if (IS (pat, STRING_C_PATTERN)) {
1757        char *q;
1758        PUSH_REF (p, row);
1759        reset_transput_buffer (EDIT_BUFFER);
1760        add_string_from_stack_transput_buffer (p, EDIT_BUFFER);
1761        q = get_transput_buffer (EDIT_BUFFER);
1762        write_c_pattern (pat, mode, (BYTE_T *) q, ref_file);
1763      } else {
1764        pattern_error (p, mode, ATTRIBUTE (pat));
1765      }
1766    } else if (IS_UNION (mode)) {
1767      A68_UNION *z = (A68_UNION *) item;
1768      genie_write_standard_format (p, (MOID_T *) (VALUE (z)), &item[A68_UNION_SIZE], ref_file, formats);
1769    } else if (IS_STRUCT (mode)) {
1770      for (PACK_T *q = PACK (mode); q != NO_PACK; FORWARD (q)) {
1771        BYTE_T *elem = &item[OFFSET (q)];
1772        genie_check_initialisation (p, elem, MOID (q));
1773        genie_write_standard_format (p, MOID (q), elem, ref_file, formats);
1774      }
1775    } else if (IS_ROW (mode) || IS_FLEX (mode)) {
1776      MOID_T *deflexed = DEFLEX (mode);
1777      CHECK_INIT (p, INITIALISED ((A68_REF *) item), M_ROWS);
1778      A68_ARRAY *arr; A68_TUPLE *tup;
1779      GET_DESCRIPTOR (arr, tup, (A68_REF *) item);
1780      if (get_row_size (tup, DIM (arr)) > 0) {
1781        BYTE_T *base_addr = DEREF (BYTE_T, &ARRAY (arr));
1782        BOOL_T done = A68_FALSE;
1783        initialise_internal_index (tup, DIM (arr));
1784        while (!done) {
1785          ADDR_T a68_index = calculate_internal_index (tup, DIM (arr));
1786          ADDR_T elem_addr = ROW_ELEMENT (arr, a68_index);
1787          BYTE_T *elem = &base_addr[elem_addr];
1788          genie_check_initialisation (p, elem, SUB (deflexed));
1789          genie_write_standard_format (p, SUB (deflexed), elem, ref_file, formats);
1790          done = increment_internal_index (tup, DIM (arr));
1791        }
1792      }
1793    }
1794    if (errno != 0) {
1795      transput_error (p, ref_file, mode);
1796    }
1797  }
1798  
1799  //! @brief PROC ([] SIMPLOUT) VOID print f, write f
1800  
1801  void genie_write_format (NODE_T * p)
1802  {
1803    A68_REF row;
1804    POP_REF (p, &row);
1805    genie_stand_out (p);
1806    PUSH_REF (p, row);
1807    genie_write_file_format (p);
1808  }
1809  
1810  //! @brief PROC (REF FILE, [] SIMPLOUT) VOID put f
1811  
1812  void genie_write_file_format (NODE_T * p)
1813  {
1814    A68_REF row;
1815    POP_REF (p, &row);
1816    CHECK_REF (p, row, M_ROW_SIMPLOUT);
1817    A68_ARRAY *arr; A68_TUPLE *tup;
1818    GET_DESCRIPTOR (arr, tup, &row);
1819    int elems = ROW_SIZE (tup);
1820    A68_REF ref_file;
1821    POP_REF (p, &ref_file);
1822    CHECK_REF (p, ref_file, M_REF_FILE);
1823    A68_FILE *file = FILE_DEREF (&ref_file);
1824    CHECK_INIT (p, INITIALISED (file), M_FILE);
1825    if (!OPENED (file)) {
1826      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
1827      exit_genie (p, A68_RUNTIME_ERROR);
1828    }
1829    if (DRAW_MOOD (file)) {
1830      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
1831      exit_genie (p, A68_RUNTIME_ERROR);
1832    }
1833    if (READ_MOOD (file)) {
1834      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "read");
1835      exit_genie (p, A68_RUNTIME_ERROR);
1836    }
1837    if (!PUT (&CHANNEL (file))) {
1838      diagnostic (A68_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "putting");
1839      exit_genie (p, A68_RUNTIME_ERROR);
1840    }
1841    if (!READ_MOOD (file) && !WRITE_MOOD (file)) {
1842      if (IS_NIL (STRING (file))) {
1843        if ((FD (file) = open_physical_file (p, ref_file, A68_WRITE_ACCESS, A68_PROTECTION)) == A68_NO_FILE) {
1844          open_error (p, ref_file, "putting");
1845        }
1846      } else {
1847        FD (file) = open_physical_file (p, ref_file, A68_WRITE_ACCESS, 0);
1848      }
1849      DRAW_MOOD (file) = A68_FALSE;
1850      READ_MOOD (file) = A68_FALSE;
1851      WRITE_MOOD (file) = A68_TRUE;
1852      CHAR_MOOD (file) = A68_TRUE;
1853    }
1854    if (!CHAR_MOOD (file)) {
1855      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "binary");
1856      exit_genie (p, A68_RUNTIME_ERROR);
1857    }
1858  // Save stack state since formats have frames.
1859    ADDR_T pop_fp = FRAME_POINTER (file), pop_sp = STACK_POINTER (file);
1860    FRAME_POINTER (file) = A68_FP;
1861    STACK_POINTER (file) = A68_SP;
1862  // Process [] SIMPLOUT.
1863    if (BODY (&FORMAT (file)) != NO_NODE) {
1864      open_format_frame (p, ref_file, &FORMAT (file), NOT_EMBEDDED_FORMAT, A68_FALSE);
1865    }
1866    if (elems <= 0) {
1867      return;
1868    }
1869    BYTE_T *base_address = DEREF (BYTE_T, &ARRAY (arr));
1870    int elem_index = 0, formats = 0;
1871    for (int k = 0; k < elems; k++) {
1872      A68_UNION *z = (A68_UNION *) & (base_address[elem_index]);
1873      MOID_T *mode = (MOID_T *) (VALUE (z));
1874      BYTE_T *item = &(base_address[elem_index + A68_UNION_SIZE]);
1875      genie_write_standard_format (p, mode, item, ref_file, &formats);
1876      elem_index += SIZE (M_SIMPLOUT);
1877    }
1878  // Empty the format to purge insertions.
1879    purge_format_write (p, ref_file);
1880    BODY (&FORMAT (file)) = NO_NODE;
1881  // Dump the buffer.
1882    write_purge_buffer (p, ref_file, FORMATTED_BUFFER);
1883  // Forget about active formats.
1884    A68_FP = FRAME_POINTER (file);
1885    A68_SP = STACK_POINTER (file);
1886    FRAME_POINTER (file) = pop_fp;
1887    STACK_POINTER (file) = pop_sp;
1888  }
1889  
1890  //! @brief Give a value error in case a character is not among expected ones.
1891  
1892  BOOL_T expect (NODE_T * p, MOID_T * m, A68_REF ref_file, const char *items, char ch)
1893  {
1894    if (strchr ((char *) items, ch) == NO_TEXT) {
1895      value_error (p, m, ref_file);
1896      return A68_FALSE;
1897    } else {
1898      return A68_TRUE;
1899    }
1900  }
1901  
1902  //! @brief Read a group of insertions.
1903  
1904  void read_insertion (NODE_T * p, A68_REF ref_file)
1905  {
1906  
1907  // Algol68G does not check whether the insertions are textually there. It just
1908  // skips them. This because we blank literals in sign moulds before the sign is
1909  // put, which is non-standard Algol68, but convenient.
1910  
1911    A68_FILE *file = FILE_DEREF (&ref_file);
1912    for (; p != NO_NODE; FORWARD (p)) {
1913      read_insertion (SUB (p), ref_file);
1914      if (IS (p, FORMAT_ITEM_L)) {
1915        BOOL_T siga = (BOOL_T) ! END_OF_FILE (file);
1916        while (siga) {
1917          int ch = read_single_char (p, ref_file);
1918          siga = (BOOL_T) ((ch != NEWLINE_CHAR) && (ch != EOF_CHAR) && !END_OF_FILE (file));
1919        }
1920      } else if (IS (p, FORMAT_ITEM_P)) {
1921        BOOL_T siga = (BOOL_T) ! END_OF_FILE (file);
1922        while (siga) {
1923          int ch = read_single_char (p, ref_file);
1924          siga = (BOOL_T) ((ch != FORMFEED_CHAR) && (ch != EOF_CHAR) && !END_OF_FILE (file));
1925        }
1926      } else if (IS (p, FORMAT_ITEM_X) || IS (p, FORMAT_ITEM_Q)) {
1927        if (!END_OF_FILE (file)) {
1928          (void) read_single_char (p, ref_file);
1929        }
1930      } else if (IS (p, FORMAT_ITEM_Y)) {
1931        PUSH_REF (p, ref_file);
1932        PUSH_VALUE (p, -1, A68_INT);
1933        genie_set (p);
1934      } else if (IS (p, LITERAL)) {
1935  // Skip characters, but don't check the literal. 
1936        int len = (int) strlen (NSYMBOL (p));
1937        while (len-- && !END_OF_FILE (file)) {
1938          (void) read_single_char (p, ref_file);
1939        }
1940      } else if (IS (p, REPLICATOR)) {
1941        int k = get_replicator_value (SUB (p), A68_TRUE);
1942        if (ATTRIBUTE (SUB_NEXT (p)) != FORMAT_ITEM_K) {
1943          for (int j = 1; j <= k; j++) {
1944            read_insertion (NEXT (p), ref_file);
1945          }
1946        } else {
1947          int pos = get_transput_buffer_index (INPUT_BUFFER);
1948          for (int j = 1; j < (k - pos); j++) {
1949            if (!END_OF_FILE (file)) {
1950              (void) read_single_char (p, ref_file);
1951            }
1952          }
1953        }
1954        return;  // From REPLICATOR, don't delete this!
1955      }
1956    }
1957  }
1958  
1959  //! @brief Read string from file according current format.
1960  
1961  void read_string_pattern (NODE_T * p, MOID_T * m, A68_REF ref_file)
1962  {
1963    for (; p != NO_NODE; FORWARD (p)) {
1964      if (IS (p, INSERTION)) {
1965        read_insertion (SUB (p), ref_file);
1966      } else if (IS (p, FORMAT_ITEM_A)) {
1967        scan_n_chars (p, 1, m, ref_file);
1968      } else if (IS (p, FORMAT_ITEM_S)) {
1969        plusab_transput_buffer (p, INPUT_BUFFER, BLANK_CHAR);
1970        return;
1971      } else if (IS (p, REPLICATOR)) {
1972        int k = get_replicator_value (SUB (p), A68_TRUE);
1973        for (int j = 1; j <= k; j++) {
1974          read_string_pattern (NEXT (p), m, ref_file);
1975        }
1976        return;
1977      } else {
1978        read_string_pattern (SUB (p), m, ref_file);
1979      }
1980    }
1981  }
1982  
1983  //! @brief Traverse choice pattern.
1984  
1985  void traverse_choice_pattern (NODE_T * p, char *str, int len, int *count, int *matches, int *first_match, BOOL_T * full_match)
1986  {
1987    for (; p != NO_NODE; FORWARD (p)) {
1988      traverse_choice_pattern (SUB (p), str, len, count, matches, first_match, full_match);
1989      if (IS (p, LITERAL)) {
1990        (*count)++;
1991        if (strncmp (NSYMBOL (p), str, (size_t) len) == 0) {
1992          (*matches)++;
1993          (*full_match) = (BOOL_T) ((*full_match) | (strcmp (NSYMBOL (p), str) == 0));
1994          if (*first_match == 0 && *full_match) {
1995            *first_match = *count;
1996          }
1997        }
1998      }
1999    }
2000  }
2001  
2002  //! @brief Read appropriate insertion from a choice pattern.
2003  
2004  int read_choice_pattern (NODE_T * p, A68_REF ref_file)
2005  {
2006  
2007  // This implementation does not have the RR peculiarity that longest
2008  // matching literal must be first, in case of non-unique first chars.
2009  
2010    A68_FILE *file = FILE_DEREF (&ref_file);
2011    BOOL_T cont = A68_TRUE;
2012    int longest_match = 0, longest_match_len = 0;
2013    while (cont) {
2014      int ch = char_scanner (file);
2015      if (!END_OF_FILE (file)) {
2016        int len, count = 0, matches = 0, first_match = 0;
2017        BOOL_T full_match = A68_FALSE;
2018        plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
2019        len = get_transput_buffer_index (INPUT_BUFFER);
2020        traverse_choice_pattern (p, get_transput_buffer (INPUT_BUFFER), len, &count, &matches, &first_match, &full_match);
2021        if (full_match && matches == 1 && first_match > 0) {
2022          return first_match;
2023        } else if (full_match && matches > 1 && first_match > 0) {
2024          longest_match = first_match;
2025          longest_match_len = len;
2026        } else if (matches == 0) {
2027          cont = A68_FALSE;
2028        }
2029      } else {
2030        cont = A68_FALSE;
2031      }
2032    }
2033    if (longest_match > 0) {
2034  // Push back look-ahead chars.
2035      if (get_transput_buffer_index (INPUT_BUFFER) > 0) {
2036        char *z = get_transput_buffer (INPUT_BUFFER);
2037        END_OF_FILE (file) = A68_FALSE;
2038        add_string_transput_buffer (p, TRANSPUT_BUFFER (file), &z[longest_match_len]);
2039      }
2040      return longest_match;
2041    } else {
2042      value_error (p, M_INT, ref_file);
2043      return 0;
2044    }
2045  }
2046  
2047  //! @brief Read value according to a general-pattern.
2048  
2049  void read_number_generic (NODE_T * p, MOID_T * mode, BYTE_T * item, A68_REF ref_file)
2050  {
2051    GENIE_UNIT (NEXT_SUB (p));
2052  // RR says to ignore parameters just calculated, so we will.
2053    A68_REF row;
2054    POP_REF (p, &row);
2055    genie_read_standard (p, mode, item, ref_file);
2056  }
2057  
2058  // INTEGRAL, REAL, COMPLEX and BITS patterns.
2059  
2060  //! @brief Read sign-mould according current format.
2061  
2062  void read_sign_mould (NODE_T * p, MOID_T * m, A68_REF ref_file, int *sign)
2063  {
2064    for (; p != NO_NODE; FORWARD (p)) {
2065      if (IS (p, INSERTION)) {
2066        read_insertion (SUB (p), ref_file);
2067      } else if (IS (p, REPLICATOR)) {
2068        int k = get_replicator_value (SUB (p), A68_TRUE);
2069        for (int j = 1; j <= k; j++) {
2070          read_sign_mould (NEXT (p), m, ref_file, sign);
2071        }
2072        return;                   // Leave this!
2073      } else {
2074        switch (ATTRIBUTE (p)) {
2075        case FORMAT_ITEM_Z:
2076        case FORMAT_ITEM_D:
2077        case FORMAT_ITEM_S:
2078        case FORMAT_ITEM_PLUS:
2079        case FORMAT_ITEM_MINUS: {
2080            int ch = read_single_char (p, ref_file);
2081  // When a sign has been read, digits are expected.
2082            if (*sign != 0) {
2083              if (expect (p, m, ref_file, INT_DIGITS, (char) ch)) {
2084                plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
2085              } else {
2086                plusab_transput_buffer (p, INPUT_BUFFER, '0');
2087              }
2088  // When a sign has not been read, a sign is expected.  If there is a digit
2089  // in stead of a sign, the digit is accepted and '+' is assumed; RR demands a
2090  // space to preceed the digit, Algol68G does not.
2091            } else {
2092              if (strchr (SIGN_DIGITS, ch) != NO_TEXT) {
2093                if (ch == '+') {
2094                  *sign = 1;
2095                } else if (ch == '-') {
2096                  *sign = -1;
2097                } else if (ch == BLANK_CHAR) {
2098                  ;
2099                }
2100              } else if (expect (p, m, ref_file, INT_DIGITS, (char) ch)) {
2101                plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
2102                *sign = 1;
2103              }
2104            }
2105            break;
2106          }
2107        default: {
2108            read_sign_mould (SUB (p), m, ref_file, sign);
2109            break;
2110          }
2111        }
2112      }
2113    }
2114  }
2115  
2116  //! @brief Read mould according current format.
2117  
2118  void read_integral_mould (NODE_T * p, MOID_T * m, A68_REF ref_file)
2119  {
2120    for (; p != NO_NODE; FORWARD (p)) {
2121      if (IS (p, INSERTION)) {
2122        read_insertion (SUB (p), ref_file);
2123      } else if (IS (p, REPLICATOR)) {
2124        int k = get_replicator_value (SUB (p), A68_TRUE);
2125        for (int j = 1; j <= k; j++) {
2126          read_integral_mould (NEXT (p), m, ref_file);
2127        }
2128        return; // Leave this!
2129      } else if (IS (p, FORMAT_ITEM_Z)) {
2130        int ch = read_single_char (p, ref_file);
2131        const char *digits = (m == M_BITS || m == M_LONG_BITS || m == M_LONG_LONG_BITS) ? BITS_DIGITS_BLANK : INT_DIGITS_BLANK;
2132        if (expect (p, m, ref_file, digits, (char) ch)) {
2133          plusab_transput_buffer (p, INPUT_BUFFER, (char) ((ch == BLANK_CHAR) ? '0' : ch));
2134        } else {
2135          plusab_transput_buffer (p, INPUT_BUFFER, '0');
2136        }
2137      } else if (IS (p, FORMAT_ITEM_D)) {
2138        int ch = read_single_char (p, ref_file);
2139        const char *digits = (m == M_BITS || m == M_LONG_BITS || m == M_LONG_LONG_BITS) ? BITS_DIGITS : INT_DIGITS;
2140        if (expect (p, m, ref_file, digits, (char) ch)) {
2141          plusab_transput_buffer (p, INPUT_BUFFER, (char) ch);
2142        } else {
2143          plusab_transput_buffer (p, INPUT_BUFFER, '0');
2144        }
2145      } else if (IS (p, FORMAT_ITEM_S)) {
2146        plusab_transput_buffer (p, INPUT_BUFFER, '0');
2147      } else {
2148        read_integral_mould (SUB (p), m, ref_file);
2149      }
2150    }
2151  }
2152  
2153  //! @brief Read mould according current format.
2154  
2155  void read_integral_pattern (NODE_T * p, MOID_T * m, BYTE_T * item, A68_REF ref_file)
2156  {
2157    NODE_T *q = SUB (p);
2158    if (q != NO_NODE && IS (q, SIGN_MOULD)) {
2159      int sign = 0;
2160      char *z;
2161      plusab_transput_buffer (p, INPUT_BUFFER, BLANK_CHAR);
2162      read_sign_mould (SUB (q), m, ref_file, &sign);
2163      z = get_transput_buffer (INPUT_BUFFER);
2164      z[0] = (char) ((sign == -1) ? '-' : '+');
2165      FORWARD (q);
2166    }
2167    if (q != NO_NODE && IS (q, INTEGRAL_MOULD)) {
2168      read_integral_mould (SUB (q), m, ref_file);
2169    }
2170    genie_string_to_value (p, m, item, ref_file);
2171  }
2172  
2173  //! @brief Read point, exponent or i-frame.
2174  
2175  void read_pie_frame (NODE_T * p, MOID_T * m, A68_REF ref_file, int att, int item, char ch)
2176  {
2177  // Widen ch to a stringlet.
2178    char sym[3];
2179    sym[0] = ch;
2180    sym[1] = (char) TO_LOWER (ch);
2181    sym[2] = NULL_CHAR;
2182  // Now read the frame.
2183    for (; p != NO_NODE; FORWARD (p)) {
2184      if (IS (p, INSERTION)) {
2185        read_insertion (p, ref_file);
2186      } else if (IS (p, att)) {
2187        read_pie_frame (SUB (p), m, ref_file, att, item, ch);
2188        return;
2189      } else if (IS (p, FORMAT_ITEM_S)) {
2190        plusab_transput_buffer (p, INPUT_BUFFER, sym[0]);
2191        return;
2192      } else if (IS (p, item)) {
2193        int ch0 = read_single_char (p, ref_file);
2194        if (expect (p, m, ref_file, sym, (char) ch0)) {
2195          plusab_transput_buffer (p, INPUT_BUFFER, sym[0]);
2196        } else {
2197          plusab_transput_buffer (p, INPUT_BUFFER, sym[0]);
2198        }
2199      }
2200    }
2201  }
2202  
2203  //! @brief Read REAL value using real pattern.
2204  
2205  void read_real_pattern (NODE_T * p, MOID_T * m, BYTE_T * item, A68_REF ref_file)
2206  {
2207  // Dive into pattern.
2208    NODE_T *q = (IS (p, REAL_PATTERN)) ? SUB (p) : p;
2209  // Dissect pattern.
2210    if (q != NO_NODE && IS (q, SIGN_MOULD)) {
2211      int sign = 0;
2212      char *z;
2213      plusab_transput_buffer (p, INPUT_BUFFER, BLANK_CHAR);
2214      read_sign_mould (SUB (q), m, ref_file, &sign);
2215      z = get_transput_buffer (INPUT_BUFFER);
2216      z[0] = (char) ((sign == -1) ? '-' : '+');
2217      FORWARD (q);
2218    }
2219    if (q != NO_NODE && IS (q, INTEGRAL_MOULD)) {
2220      read_integral_mould (SUB (q), m, ref_file);
2221      FORWARD (q);
2222    }
2223    if (q != NO_NODE && IS (q, FORMAT_POINT_FRAME)) {
2224      read_pie_frame (SUB (q), m, ref_file, FORMAT_POINT_FRAME, FORMAT_ITEM_POINT, POINT_CHAR);
2225      FORWARD (q);
2226    }
2227    if (q != NO_NODE && IS (q, INTEGRAL_MOULD)) {
2228      read_integral_mould (SUB (q), m, ref_file);
2229      FORWARD (q);
2230    }
2231    if (q != NO_NODE && IS (q, EXPONENT_FRAME)) {
2232      read_pie_frame (SUB (q), m, ref_file, FORMAT_E_FRAME, FORMAT_ITEM_E, EXPONENT_CHAR);
2233      q = NEXT_SUB (q);
2234      if (q != NO_NODE && IS (q, SIGN_MOULD)) {
2235        int k, sign = 0;
2236        char *z;
2237        plusab_transput_buffer (p, INPUT_BUFFER, BLANK_CHAR);
2238        k = get_transput_buffer_index (INPUT_BUFFER);
2239        read_sign_mould (SUB (q), m, ref_file, &sign);
2240        z = get_transput_buffer (INPUT_BUFFER);
2241        z[k - 1] = (char) ((sign == -1) ? '-' : '+');
2242        FORWARD (q);
2243      }
2244      if (q != NO_NODE && IS (q, INTEGRAL_MOULD)) {
2245        read_integral_mould (SUB (q), m, ref_file);
2246        FORWARD (q);
2247      }
2248    }
2249    genie_string_to_value (p, m, item, ref_file);
2250  }
2251  
2252  //! @brief Read COMPLEX value using complex pattern.
2253  
2254  void read_complex_pattern (NODE_T * p, MOID_T * comp, MOID_T * m, BYTE_T * re, BYTE_T * im, A68_REF ref_file)
2255  {
2256  // Dissect pattern.
2257    NODE_T *reel = SUB (p);
2258    NODE_T *plus_i_times = NEXT (reel);
2259    NODE_T *imag = NEXT (plus_i_times);
2260  // Read pattern.
2261    read_real_pattern (reel, m, re, ref_file);
2262    reset_transput_buffer (INPUT_BUFFER);
2263    read_pie_frame (plus_i_times, comp, ref_file, FORMAT_I_FRAME, FORMAT_ITEM_I, 'I');
2264    reset_transput_buffer (INPUT_BUFFER);
2265    read_real_pattern (imag, m, im, ref_file);
2266  }
2267  
2268  //! @brief Read BITS value according pattern.
2269  
2270  void read_bits_pattern (NODE_T * p, MOID_T * m, BYTE_T * item, A68_REF ref_file)
2271  {
2272    int radix = get_replicator_value (SUB_SUB (p), A68_TRUE);
2273    if (radix < 2 || radix > 16) {
2274      diagnostic (A68_RUNTIME_ERROR, p, ERROR_INVALID_RADIX, radix);
2275      exit_genie (p, A68_RUNTIME_ERROR);
2276    }
2277    char *z = get_transput_buffer (INPUT_BUFFER);
2278    ASSERT (a68_bufprt (z, (size_t) TRANSPUT_BUFFER_SIZE, "%dr", radix) >= 0);
2279    set_transput_buffer_index (INPUT_BUFFER, (int) strlen (z));
2280    read_integral_mould (NEXT_SUB (p), m, ref_file);
2281    genie_string_to_value (p, m, item, ref_file);
2282  }
2283  
2284  //! @brief Read object with from file and store.
2285  
2286  void genie_read_real_format (NODE_T * p, MOID_T * mode, BYTE_T * item, A68_REF ref_file)
2287  {
2288    if (IS (p, GENERAL_PATTERN) && NEXT_SUB (p) == NO_NODE) {
2289      genie_read_standard (p, mode, item, ref_file);
2290    } else if (IS (p, GENERAL_PATTERN) && NEXT_SUB (p) != NO_NODE) {
2291      read_number_generic (p, mode, item, ref_file);
2292    } else if (IS (p, FIXED_C_PATTERN) || IS (p, FLOAT_C_PATTERN) || IS (p, GENERAL_C_PATTERN)) {
2293      read_c_pattern (p, mode, item, ref_file);
2294    } else if (IS (p, REAL_PATTERN)) {
2295      read_real_pattern (p, mode, item, ref_file);
2296    } else {
2297      pattern_error (p, mode, ATTRIBUTE (p));
2298    }
2299  }
2300  
2301  //! @brief At end of read purge all insertions.
2302  
2303  void purge_format_read (NODE_T * p, A68_REF ref_file)
2304  {
2305    BOOL_T siga;
2306    do {
2307      NODE_T *pat;
2308      while ((pat = get_next_format_pattern (p, ref_file, SKIP_PATTERN)) != NO_NODE) {
2309        format_error (p, ref_file, ERROR_FORMAT_PICTURES);
2310      }
2311      A68_FILE *file = FILE_DEREF (&ref_file);
2312      NODE_T *dollar = SUB (BODY (&FORMAT (file)));
2313      A68_FORMAT *old_fmt = (A68_FORMAT *) FRAME_LOCAL (A68_FP, OFFSET (TAX (dollar)));
2314      siga = (BOOL_T) ! IS_NIL_FORMAT (old_fmt);
2315      if (siga) {
2316  // Pop embedded format and proceed.
2317        (void) end_of_format (p, ref_file);
2318      }
2319    } while (siga);
2320  }
2321  
2322  //! @brief Read object with from file and store.
2323  
2324  void genie_read_standard_format (NODE_T * p, MOID_T * mode, BYTE_T * item, A68_REF ref_file, int *formats)
2325  {
2326    errno = 0;
2327    reset_transput_buffer (INPUT_BUFFER);
2328    if (mode == M_FORMAT) {
2329      CHECK_REF (p, ref_file, M_REF_FILE);
2330      A68_FILE *file = FILE_DEREF (&ref_file);
2331  // Forget about eventual active formats and set up new one.
2332      if (*formats > 0) {
2333        purge_format_read (p, ref_file);
2334      }
2335      (*formats)++;
2336      A68_FP = FRAME_POINTER (file);
2337      A68_SP = STACK_POINTER (file);
2338      open_format_frame (p, ref_file, (A68_FORMAT *) item, NOT_EMBEDDED_FORMAT, A68_TRUE);
2339    } else if (mode == M_PROC_REF_FILE_VOID) {
2340      diagnostic (A68_RUNTIME_ERROR, p, ERROR_UNDEFINED_TRANSPUT, M_PROC_REF_FILE_VOID);
2341      exit_genie (p, A68_RUNTIME_ERROR);
2342    } else if (mode == M_REF_SOUND) {
2343      diagnostic (A68_RUNTIME_ERROR, p, ERROR_UNDEFINED_TRANSPUT, M_REF_SOUND);
2344      exit_genie (p, A68_RUNTIME_ERROR);
2345    } else if (IS_REF (mode)) {
2346      CHECK_REF (p, *(A68_REF *) item, mode);
2347      genie_read_standard_format (p, SUB (mode), ADDRESS ((A68_REF *) item), ref_file, formats);
2348    } else if (mode == M_INT || mode == M_LONG_INT || mode == M_LONG_LONG_INT) {
2349      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
2350      if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) == NO_NODE) {
2351        genie_read_standard (pat, mode, item, ref_file);
2352      } else if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) != NO_NODE) {
2353        read_number_generic (pat, mode, item, ref_file);
2354      } else if (IS (pat, INTEGRAL_C_PATTERN)) {
2355        read_c_pattern (pat, mode, item, ref_file);
2356      } else if (IS (pat, INTEGRAL_PATTERN)) {
2357        read_integral_pattern (pat, mode, item, ref_file);
2358      } else if (IS (pat, CHOICE_PATTERN)) {
2359        int k = read_choice_pattern (pat, ref_file);
2360        if (mode == M_INT) {
2361          A68_INT *z = (A68_INT *) item;
2362          VALUE (z) = k;
2363          STATUS (z) = (STATUS_MASK_T) ((VALUE (z) > 0) ? INIT_MASK : NULL_MASK);
2364        } else {
2365          diagnostic (A68_RUNTIME_ERROR, p, ERROR_DEPRECATED, mode);
2366          exit_genie (p, A68_RUNTIME_ERROR);
2367        }
2368      } else {
2369        pattern_error (p, mode, ATTRIBUTE (pat));
2370      }
2371    } else if (mode == M_REAL || mode == M_LONG_REAL || mode == M_LONG_LONG_REAL) {
2372      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
2373      genie_read_real_format (pat, mode, item, ref_file);
2374    } else if (mode == M_COMPLEX) {
2375      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
2376      if (IS (pat, COMPLEX_PATTERN)) {
2377        read_complex_pattern (pat, mode, M_REAL, item, &item[SIZE (M_REAL)], ref_file);
2378      } else {
2379  // Try reading as two REAL values.
2380        genie_read_real_format (pat, M_REAL, item, ref_file);
2381        genie_read_standard_format (p, M_REAL, &item[SIZE (M_REAL)], ref_file, formats);
2382      }
2383    } else if (mode == M_LONG_COMPLEX) {
2384      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
2385      if (IS (pat, COMPLEX_PATTERN)) {
2386        read_complex_pattern (pat, mode, M_LONG_REAL, item, &item[SIZE (M_LONG_REAL)], ref_file);
2387      } else {
2388  // Try reading as two LONG REAL values.
2389        genie_read_real_format (pat, M_LONG_REAL, item, ref_file);
2390        genie_read_standard_format (p, M_LONG_REAL, &item[SIZE (M_LONG_REAL)], ref_file, formats);
2391      }
2392    } else if (mode == M_LONG_LONG_COMPLEX) {
2393      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
2394      if (IS (pat, COMPLEX_PATTERN)) {
2395        read_complex_pattern (pat, mode, M_LONG_LONG_REAL, item, &item[SIZE (M_LONG_LONG_REAL)], ref_file);
2396      } else {
2397  // Try reading as two LONG LONG REAL values.
2398        genie_read_real_format (pat, M_LONG_LONG_REAL, item, ref_file);
2399        genie_read_standard_format (p, M_LONG_LONG_REAL, &item[SIZE (M_LONG_LONG_REAL)], ref_file, formats);
2400      }
2401    } else if (mode == M_BOOL) {
2402      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
2403      if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) == NO_NODE) {
2404        genie_read_standard (p, mode, item, ref_file);
2405      } else if (IS (pat, BOOLEAN_PATTERN)) {
2406        if (NEXT_SUB (pat) == NO_NODE) {
2407          genie_read_standard (p, mode, item, ref_file);
2408        } else {
2409          A68_BOOL *z = (A68_BOOL *) item;
2410          int k = read_choice_pattern (pat, ref_file);
2411          if (k == 1 || k == 2) {
2412            VALUE (z) = (BOOL_T) ((k == 1) ? A68_TRUE : A68_FALSE);
2413            STATUS (z) = INIT_MASK;
2414          } else {
2415            STATUS (z) = NULL_MASK;
2416          }
2417        }
2418      } else {
2419        pattern_error (p, mode, ATTRIBUTE (pat));
2420      }
2421    } else if (mode == M_BITS || mode == M_LONG_BITS || mode == M_LONG_LONG_BITS) {
2422      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
2423      if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) == NO_NODE) {
2424        genie_read_standard (p, mode, item, ref_file);
2425      } else if (IS (pat, BITS_PATTERN)) {
2426        read_bits_pattern (pat, mode, item, ref_file);
2427      } else if (IS (pat, BITS_C_PATTERN)) {
2428        read_c_pattern (pat, mode, item, ref_file);
2429      } else {
2430        pattern_error (p, mode, ATTRIBUTE (pat));
2431      }
2432    } else if (mode == M_CHAR) {
2433      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
2434      if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) == NO_NODE) {
2435        genie_read_standard (p, mode, item, ref_file);
2436      } else if (IS (pat, STRING_PATTERN)) {
2437        read_string_pattern (pat, M_CHAR, ref_file);
2438        genie_string_to_value (p, mode, item, ref_file);
2439      } else if (IS (pat, CHAR_C_PATTERN)) {
2440        read_c_pattern (pat, mode, item, ref_file);
2441      } else {
2442        pattern_error (p, mode, ATTRIBUTE (pat));
2443      }
2444    } else if (mode == M_ROW_CHAR || mode == M_STRING) {
2445  // Handle these separately instead of reading [] CHAR.
2446      NODE_T *pat = get_next_format_pattern (p, ref_file, WANT_PATTERN);
2447      if (IS (pat, GENERAL_PATTERN) && NEXT_SUB (pat) == NO_NODE) {
2448        genie_read_standard (p, mode, item, ref_file);
2449      } else if (IS (pat, STRING_PATTERN)) {
2450        read_string_pattern (pat, mode, ref_file);
2451        genie_string_to_value (p, mode, item, ref_file);
2452      } else if (IS (pat, STRING_C_PATTERN)) {
2453        read_c_pattern (pat, mode, item, ref_file);
2454      } else {
2455        pattern_error (p, mode, ATTRIBUTE (pat));
2456      }
2457    } else if (IS_UNION (mode)) {
2458      A68_UNION *z = (A68_UNION *) item;
2459      genie_read_standard_format (p, (MOID_T *) (VALUE (z)), &item[A68_UNION_SIZE], ref_file, formats);
2460    } else if (IS_STRUCT (mode)) {
2461      for (PACK_T *q = PACK (mode); q != NO_PACK; FORWARD (q)) {
2462        BYTE_T *elem = &item[OFFSET (q)];
2463        genie_read_standard_format (p, MOID (q), elem, ref_file, formats);
2464      }
2465    } else if (IS_ROW (mode) || IS_FLEX (mode)) {
2466      MOID_T *deflexed = DEFLEX (mode);
2467      A68_ARRAY *arr;
2468      A68_TUPLE *tup;
2469      CHECK_INIT (p, INITIALISED ((A68_REF *) item), M_ROWS);
2470      GET_DESCRIPTOR (arr, tup, (A68_REF *) item);
2471      if (get_row_size (tup, DIM (arr)) > 0) {
2472        BYTE_T *base_addr = DEREF (BYTE_T, &ARRAY (arr));
2473        BOOL_T done = A68_FALSE;
2474        initialise_internal_index (tup, DIM (arr));
2475        while (!done) {
2476          ADDR_T a68_index = calculate_internal_index (tup, DIM (arr));
2477          ADDR_T elem_addr = ROW_ELEMENT (arr, a68_index);
2478          BYTE_T *elem = &base_addr[elem_addr];
2479          genie_read_standard_format (p, SUB (deflexed), elem, ref_file, formats);
2480          done = increment_internal_index (tup, DIM (arr));
2481        }
2482      }
2483    }
2484    if (errno != 0) {
2485      transput_error (p, ref_file, mode);
2486    }
2487  }
2488  
2489  //! @brief PROC ([] SIMPLIN) VOID read f
2490  
2491  void genie_read_format (NODE_T * p)
2492  {
2493    A68_REF row;
2494    POP_REF (p, &row);
2495    genie_stand_in (p);
2496    PUSH_REF (p, row);
2497    genie_read_file_format (p);
2498  }
2499  
2500  //! @brief PROC (REF FILE, [] SIMPLIN) VOID get f
2501  
2502  void genie_read_file_format (NODE_T * p)
2503  {
2504    A68_REF row;
2505    POP_REF (p, &row);
2506    CHECK_REF (p, row, M_ROW_SIMPLIN);
2507    A68_ARRAY *arr; A68_TUPLE *tup;
2508    GET_DESCRIPTOR (arr, tup, &row);
2509    int elems = ROW_SIZE (tup);
2510    A68_REF ref_file;
2511    POP_REF (p, &ref_file);
2512    CHECK_REF (p, ref_file, M_REF_FILE);
2513    A68_FILE *file = FILE_DEREF (&ref_file);
2514    CHECK_INIT (p, INITIALISED (file), M_FILE);
2515    if (!OPENED (file)) {
2516      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_NOT_OPEN);
2517      exit_genie (p, A68_RUNTIME_ERROR);
2518    }
2519    if (DRAW_MOOD (file)) {
2520      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "draw");
2521      exit_genie (p, A68_RUNTIME_ERROR);
2522    }
2523    if (WRITE_MOOD (file)) {
2524      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "write");
2525      exit_genie (p, A68_RUNTIME_ERROR);
2526    }
2527    if (!GET (&CHANNEL (file))) {
2528      diagnostic (A68_RUNTIME_ERROR, p, ERROR_CHANNEL_DOES_NOT_ALLOW, "getting");
2529      exit_genie (p, A68_RUNTIME_ERROR);
2530    }
2531    if (!READ_MOOD (file) && !WRITE_MOOD (file)) {
2532      if (IS_NIL (STRING (file))) {
2533        if ((FD (file) = open_physical_file (p, ref_file, A68_READ_ACCESS, 0)) == A68_NO_FILE) {
2534          open_error (p, ref_file, "getting");
2535        }
2536      } else {
2537        FD (file) = open_physical_file (p, ref_file, A68_READ_ACCESS, 0);
2538      }
2539      DRAW_MOOD (file) = A68_FALSE;
2540      READ_MOOD (file) = A68_TRUE;
2541      WRITE_MOOD (file) = A68_FALSE;
2542      CHAR_MOOD (file) = A68_TRUE;
2543    }
2544    if (!CHAR_MOOD (file)) {
2545      diagnostic (A68_RUNTIME_ERROR, p, ERROR_FILE_WRONG_MOOD, "binary");
2546      exit_genie (p, A68_RUNTIME_ERROR);
2547    }
2548  // Save stack state since formats have frames.
2549    ADDR_T pop_fp = FRAME_POINTER (file), pop_sp = STACK_POINTER (file);
2550    FRAME_POINTER (file) = A68_FP;
2551    STACK_POINTER (file) = A68_SP;
2552  // Process [] SIMPLIN.
2553    if (BODY (&FORMAT (file)) != NO_NODE) {
2554      open_format_frame (p, ref_file, &FORMAT (file), NOT_EMBEDDED_FORMAT, A68_FALSE);
2555    }
2556    if (elems <= 0) {
2557      return;
2558    }
2559    int elem_index = 0, formats = 0;
2560    BYTE_T *base_address = DEREF (BYTE_T, &ARRAY (arr));
2561    for (int k = 0; k < elems; k++) {
2562      A68_UNION *z = (A68_UNION *) & (base_address[elem_index]);
2563      MOID_T *mode = (MOID_T *) (VALUE (z));
2564      BYTE_T *item = (BYTE_T *) & (base_address[elem_index + A68_UNION_SIZE]);
2565      genie_read_standard_format (p, mode, item, ref_file, &formats);
2566      elem_index += SIZE (M_SIMPLIN);
2567    }
2568  // Empty the format to purge insertions.
2569    purge_format_read (p, ref_file);
2570    BODY (&FORMAT (file)) = NO_NODE;
2571  // Forget about active formats.
2572    A68_FP = FRAME_POINTER (file);
2573    A68_SP = STACK_POINTER (file);
2574    FRAME_POINTER (file) = pop_fp;
2575    STACK_POINTER (file) = pop_sp;
2576  }