/* file fsaio.c - 11. 10. 94.
 * 9/1/98. Change generator type from char to `gen'.
 * 1.5.96.  - adjusted kbm_type_names, srec_read and srec_print to deal with
 *            srec type "list of Integers".
 * This file contains io-routines for manipulating finite state automata
 * They can deal with both deterministic and non-deterministic automata,
 * but the functions in fsa.c currently only deal with dfa's.
 *
 * 13.9.95. - wrote in code to handle srec type "list of words"
 * 5.12.94. - changes for sparse storage type with some dense rows.
 * 17.11.94. Added parts to do with labeled srec type.
 */

#define MAXWORDLEN 65535

#include <stdlib.h>
#include "defs.h"
#include "fsa.h"
#include "externals.h"

char *kbm_type_names[] = {"simple",        "identifiers",      "words",
                          "list of words", "list of integers", "strings",
                          "labeled",       "product"};
char *kbm_flag_names[] = {"DFA", "NFA",        "MIDFA", "minimized",
                          "BFS", "accessible", "trim",  "RWS"};
static gen *wbuffer; /* used only for calls of read_word - this can
                      * be allocated temporarily.
                      */

/* Functions defined in this file: */


/* Print the set record *srptr. Follows corresponding GAP routine.
 * Currently, rather arbitrarily, identifiers and strings names are
 * printed in dense format, and words and lists of words in sparse format.
 */
void srec_print(FILE *wfile, srec *srptr, char *name, int offset,
                char *endstring)

{
  int ct, j, l;
  srec sr;
  boolean first;

  sr = *srptr;
  *kbm_buffer = '\0';
  if (stringlen(name) == 0)
    add_to_buffer(0, "rec(");
  else {
    add_to_buffer(12 + offset, name);
    add_to_buffer(0, " := rec(");
  }

  printbuffer(wfile);
  add_to_buffer(16 + offset, "type");
  sprintf(kbm_buffer + stringlen(kbm_buffer), " := \"%s\",",
          kbm_type_names[sr.type]);
  printbuffer(wfile);

  add_to_buffer(16 + offset, "size");
  sprintf(kbm_buffer + stringlen(kbm_buffer), " := %d", sr.size);
  if (sr.type != SIMPLE)
    add_to_buffer(0, ",");
  printbuffer(wfile);

  if (sr.type == PRODUCT) {
    add_to_buffer(16 + offset, "arity");
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := %d,", sr.arity);
    printbuffer(wfile);

    add_to_buffer(16 + offset, "padding");
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := %c,", sr.padding);
    printbuffer(wfile);

    srec_print(wfile, sr.base, "base", offset + 4, " ");
  }

  else if (sr.type == IDENTIFIERS) {
    add_to_buffer(16 + offset, "format");
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := \"dense\",");
    printbuffer(wfile);

    add_to_buffer(16 + offset, "names");
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := [");
    ct = 1;
    while (ct <= sr.size) {
      if (ct == 1 || stringlen(kbm_buffer) + stringlen(sr.names[ct]) <= 76) {
        if (ct > 1)
          add_to_buffer(0, ",");
        sprintf(kbm_buffer + stringlen(kbm_buffer), "%s", sr.names[ct]);
      }
      else {
        add_to_buffer(0, ",");
        printbuffer(wfile);
        add_to_buffer(21 + offset, "");
        sprintf(kbm_buffer + stringlen(kbm_buffer), "%s", sr.names[ct]);
      }
      ct++;
    }
    add_to_buffer(0, "]");
    printbuffer(wfile);
  }

  else if (sr.type == STRINGS) {
    add_to_buffer(16 + offset, "format");
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := \"dense\",");
    printbuffer(wfile);

    add_to_buffer(16 + offset, "names");
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := [");
    ct = 1;
    while (ct <= sr.size) {
      if (ct == 1 ||
          stringlen(kbm_buffer) + stringlen(sr.names[ct]) + 2 <= 76) {
        if (ct > 1)
          add_to_buffer(0, ",");
        sprintf(kbm_buffer + stringlen(kbm_buffer), "\"%s\"", sr.names[ct]);
      }
      else {
        add_to_buffer(0, ",");
        printbuffer(wfile);
        add_to_buffer(21 + offset, "");
        sprintf(kbm_buffer + stringlen(kbm_buffer), "\"%s\"", sr.names[ct]);
      }
      ct++;
    }
    add_to_buffer(0, "]");
    printbuffer(wfile);
  }

  else if (sr.type == WORDS || sr.type == LISTOFWORDS) {
    add_to_buffer(16 + offset, "alphabet");
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := [");
    ct = 1;
    while (ct <= sr.alphabet_size) {
      if (ct == 1 || stringlen(kbm_buffer) + stringlen(sr.alphabet[ct]) <= 76) {
        if (ct > 1)
          add_to_buffer(0, ",");
        sprintf(kbm_buffer + stringlen(kbm_buffer), "%s", sr.alphabet[ct]);
      }
      else {
        add_to_buffer(0, ",");
        printbuffer(wfile);
        add_to_buffer(21 + offset, "");
        sprintf(kbm_buffer + stringlen(kbm_buffer), "%s", sr.alphabet[ct]);
      }
      ct++;
    }
    add_to_buffer(0, "],");
    printbuffer(wfile);

    add_to_buffer(16 + offset, "format");
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := \"sparse\",");
    printbuffer(wfile);

    add_to_buffer(16 + offset, "names");
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := [");
    printbuffer(wfile);
    for (ct = 1; ct <= sr.size; ct++) {
      /* If the set is large, we will format less nicely, to save space. */
      if (sr.size > 128)
        add_to_buffer(0, "\t");
      else
        add_to_buffer(12 + offset, "");
      sprintf(kbm_buffer + stringlen(kbm_buffer), "[%d,", ct);
      if (sr.type == WORDS)
        add_word_to_buffer(wfile, sr.words[ct], sr.alphabet);
      else {
        add_to_buffer(0, "[");
        j = 0;
        while (sr.wordslist[ct][j]) {
          if (j > 0)
            add_to_buffer(0, ",");
          if (stringlen(kbm_buffer) > 68) {
            printbuffer(wfile);
            if (sr.size > 128)
              add_to_buffer(0, "\t      ");
            else
              add_to_buffer(18 + offset, "");
          }
          add_word_to_buffer(wfile, sr.wordslist[ct][j], sr.alphabet);
          j++;
        }
        add_to_buffer(0, "]");
      }
      if (ct == sr.size)
        add_to_buffer(0, "]");
      else
        add_to_buffer(0, "],");
      printbuffer(wfile);
    }
    add_to_buffer(11 + offset, "]");
    printbuffer(wfile);
  }

  else if (sr.type == LISTOFINTS) {
    add_to_buffer(16 + offset, "format");
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := \"sparse\",");
    printbuffer(wfile);

    add_to_buffer(16 + offset, "names");
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := [");
    printbuffer(wfile);
    for (ct = 1; ct <= sr.size; ct++) {
      /* If the set is large, we will format less nicely, to save space. */
      if (sr.size > 128)
        add_to_buffer(0, "\t");
      else
        add_to_buffer(12 + offset, "");
      sprintf(kbm_buffer + stringlen(kbm_buffer), "[%d,", ct);
      add_to_buffer(0, "[");
      l = sr.intlist[ct][0];
      for (j = 1; j <= l; j++) {
        if (j > 1)
          add_to_buffer(0, ",");
        if (stringlen(kbm_buffer) > 74) {
          printbuffer(wfile);
          if (sr.size > 128)
            add_to_buffer(0, "\t      ");
          else
            add_to_buffer(18 + offset, "");
        }
        sprintf(kbm_buffer + stringlen(kbm_buffer), "%d", sr.intlist[ct][j]);
      }
      add_to_buffer(0, "]");
      if (ct == sr.size)
        add_to_buffer(0, "]");
      else
        add_to_buffer(0, "],");
      printbuffer(wfile);
    }
    add_to_buffer(11 + offset, "]");
    printbuffer(wfile);
  }

  else if (sr.type == LABELED) {
    srec_print(wfile, sr.labels, "labels", offset + 4, ",");

    add_to_buffer(16 + offset, "format");
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := \"sparse\",");
    printbuffer(wfile);

    add_to_buffer(16 + offset, "setToLabels");
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := [");
    printbuffer(wfile);
    first = TRUE;
    for (ct = 1; ct <= sr.size; ct++)
      if (sr.setToLabels[ct] != 0) {
        if (first)
          first = FALSE;
        else {
          strcat(kbm_buffer, ",");
          printbuffer(wfile);
        }
        if (sr.size <= 128)
          add_to_buffer(21 + offset, "");
        else
          add_to_buffer(0, "\t");
        sprintf(kbm_buffer + stringlen(kbm_buffer), "[%d,%d]", ct,
                sr.setToLabels[ct]);
      }
    printbuffer(wfile);
    add_to_buffer(21 + offset, "]");
    printbuffer(wfile);
  }

  if (stringlen(name) == 0)
    add_to_buffer(0, ")");
  else
    add_to_buffer(12 + offset, ")");
  add_to_buffer(0, endstring);
  printbuffer(wfile);
}

/* Print the table record *tableptr. */
void table_print(FILE *wfile, table_struc *tableptr, char *name, int offset,
                 char *endstring, int ns, int ne)

{
  int **table, ct, g, i, k, nl, dr, *ptr, *ptre;
  boolean dense, densepf, first, firstg;

  table = tableptr->table_data_ptr;
  dense = tableptr->table_type == DENSE;
  densepf = tableptr->printing_format == DENSE;
  dr = tableptr->denserows;

  *kbm_buffer = '\0';
  if (stringlen(name) == 0)
    add_to_buffer(0, "rec(");
  else {
    add_to_buffer(12 + offset, name);
    add_to_buffer(0, " := rec(");
  }

  printbuffer(wfile);

  /* Calculate number of nonzero transitions - this ought to be stored in
   * tableptr->numTransitions, but we won't rely on that (unless the table
   * is stored externally).
   */

  add_to_buffer(offset + 16, "format");
  if (densepf)
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := \"dense deterministic\",");
  else
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := \"sparse\",");
  printbuffer(wfile);

  if (tableptr->filename == 0) {
    if (dense) {
      ct = 0;
      for (g = 1; g <= ne; g++)
        for (i = 1; i <= ns; i++)
          if (target(dense, table, g, i, dr) != 0)
            ct++;
    }
    else {
      ct = 0;
      for (g = 1; g <= ne; g++)
        for (i = 1; i <= dr; i++)
          if (target(dense, table, g, i, dr) != 0)
            ct++;
      if (dr < ns)
        ct += (table[ns + 1] - table[dr + 1]) / 2;
    }
    tableptr->numTransitions = ct;
  }

  add_to_buffer(offset + 16, "numTransitions");
  sprintf(kbm_buffer + stringlen(kbm_buffer), " := %d,",
          tableptr->numTransitions);
  printbuffer(wfile);

  if (tableptr->filename) {
    add_to_buffer(offset + 16, "filename");
    sprintf(kbm_buffer + stringlen(kbm_buffer), " := \"%s\"",
            tableptr->filename);
    printbuffer(wfile);
    if (stringlen(name) == 0)
      add_to_buffer(0, ")");
    else
      add_to_buffer(12 + offset, ")");
    add_to_buffer(0, endstring);
    printbuffer(wfile);
    return;
  }

  add_to_buffer(offset + 16, "transitions");
  sprintf(kbm_buffer + stringlen(kbm_buffer), " := [");
  if (ns == 0 || ns > 128)
    printbuffer(wfile);
  first = TRUE;
  for (i = 1; i <= ns; i++) {
    if (!first && ns <= 128)
      add_to_buffer(offset + 21, "");
    sprintf(kbm_buffer + stringlen(kbm_buffer), "[");
    first = FALSE;
    if (!dense && !densepf && i > dr) {
      /* In this case we can print directly */
      ptr = table[i];
      ptre = table[i + 1];
      firstg = TRUE;
      while (ptr < ptre) {
        if (!firstg)
          sprintf(kbm_buffer + stringlen(kbm_buffer), ",");
        firstg = FALSE;
        g = *(ptr++);
        k = *(ptr++);
        nl = int_len(k) + 3 + int_len((int)g);
        if (stringlen(kbm_buffer) + nl >= 76) {
          printbuffer(wfile);
          /* If the state-set is large, we format less nicely to save space. */
          if (ns <= 128)
            add_to_buffer(offset + 22, "");
          else
            add_to_buffer(0, " ");
        }
        sprintf(kbm_buffer + stringlen(kbm_buffer), "[%d,%d]", g, k);
      }
    }
    else {
      firstg = TRUE;
      for (g = 1; g <= ne; g++) {
        k = target(dense, table, g, i, dr);
        if (densepf || k != 0) {
          if (!firstg)
            sprintf(kbm_buffer + stringlen(kbm_buffer), ",");
          firstg = FALSE;
          nl = densepf ? int_len(k) : int_len(k) + 3 + int_len((int)g);
          if (stringlen(kbm_buffer) + nl >= 76) {
            printbuffer(wfile);
            if (ns <= 128)
              add_to_buffer(offset + 22, "");
            else
              add_to_buffer(0, " ");
          }
          if (densepf)
            sprintf(kbm_buffer + stringlen(kbm_buffer), "%d", k);
          else
            sprintf(kbm_buffer + stringlen(kbm_buffer), "[%d,%d]", g, k);
        }
      }
    }
    if (i < ns)
      sprintf(kbm_buffer + stringlen(kbm_buffer), "],");
    else
      sprintf(kbm_buffer + stringlen(kbm_buffer), "] ");
    if (tableptr->comment_state_numbers)
      sprintf(kbm_buffer + stringlen(kbm_buffer), "#%d", i);
    printbuffer(wfile);
  }
  add_to_buffer(offset + 20, "");
  sprintf(kbm_buffer + stringlen(kbm_buffer), "]");
  printbuffer(wfile);

  if (stringlen(name) == 0)
    add_to_buffer(0, ")");
  else
    add_to_buffer(12 + offset, ")");
  add_to_buffer(0, endstring);
  printbuffer(wfile);
}

void fsa_print(FILE *wfile, fsa *fsaptr, char *name)
{
  int i, ns, ne;
  boolean first;

  if (kbm_print_level >= 3)
    printf("    #Calling fsa_print.\n");
  ns = fsaptr->states->size;
  ne = fsaptr->alphabet->size;

  *kbm_buffer = '\0';
  if (stringlen(name) == 0)
    add_to_buffer(0, "rec(");
  else {
    sprintf(kbm_buffer, "%s := rec(", name);
  }
  printbuffer(wfile);

  add_to_buffer(16, "isFSA");
  sprintf(kbm_buffer + stringlen(kbm_buffer), " := true,");
  printbuffer(wfile);

  srec_print(wfile, fsaptr->alphabet, "alphabet", 4, ",");

  srec_print(wfile, fsaptr->states, "states", 4, ",");

  add_to_buffer(16, "flags");
  sprintf(kbm_buffer + stringlen(kbm_buffer), " := [");
  first = TRUE;
  for (i = 0; i < num_kbm_flag_strings; i++)
    if (fsaptr->flags[i]) {
      if (!first)
        sprintf(kbm_buffer + stringlen(kbm_buffer), ",");
      first = FALSE;
      sprintf(kbm_buffer + stringlen(kbm_buffer), "\"%s\"", kbm_flag_names[i]);
    }
  sprintf(kbm_buffer + stringlen(kbm_buffer), "],");
  printbuffer(wfile);

  add_to_buffer(16, "initial");
  sprintf(kbm_buffer + stringlen(kbm_buffer), " := [");
  if (ns > 1 && ns == fsaptr->num_initial)
    sprintf(kbm_buffer + stringlen(kbm_buffer), "1..%d", fsaptr->states->size);
  else {
    for (i = 1; i <= fsaptr->num_initial; i++) {
      if (i > 1)
        sprintf(kbm_buffer + stringlen(kbm_buffer), ",");
      if (stringlen(kbm_buffer) + int_len(fsaptr->initial[i]) > 76) {
        printbuffer(wfile);
        add_to_buffer(21, "");
      }
      sprintf(kbm_buffer + stringlen(kbm_buffer), "%d", fsaptr->initial[i]);
    }
  }
  sprintf(kbm_buffer + stringlen(kbm_buffer), "],");
  printbuffer(wfile);

  add_to_buffer(16, "accepting");
  sprintf(kbm_buffer + stringlen(kbm_buffer), " := [");
  if (ns > 1 && ns == fsaptr->num_accepting)
    sprintf(kbm_buffer + stringlen(kbm_buffer), "1..%d", fsaptr->states->size);
  else {
    for (i = 1; i <= fsaptr->num_accepting; i++) {
      if (i > 1)
        sprintf(kbm_buffer + stringlen(kbm_buffer), ",");
      if (stringlen(kbm_buffer) + int_len(fsaptr->accepting[i]) > 76) {
        printbuffer(wfile);
        add_to_buffer(21, "");
      }
      sprintf(kbm_buffer + stringlen(kbm_buffer), "%d", fsaptr->accepting[i]);
    }
  }
  sprintf(kbm_buffer + stringlen(kbm_buffer), "],");
  printbuffer(wfile);

  /* If the fsa is not known to be deterministic (or (25.7.95.) possibly
   * multiple start-state deterministic), we will print it sparsely
   */
  if (!fsaptr->flags[DFA] && !fsaptr->flags[MIDFA])
    fsaptr->table->printing_format = SPARSE;
  table_print(wfile, fsaptr->table, "table", 4, "", ns, ne);

  sprintf(kbm_buffer, ");");
  printbuffer(wfile);
}

/* Read the set record *srptr from rfile, assigning space as required.
 * If maxsize is larger than srptr->size, and space is allocated for
 * names or labels, then space is allocated for maxsize of these.
 * This allows for possible later expansion.
 */
void srec_read(FILE *rfile, srec *srptr, int maxsize)

{
  int delim, i, j, k, l, ct, n;
  boolean typeset, sizeset, baseset, arityset, paddingset, namesset, formatset,
      labelsset, alphabetset, setToLabelsset, nonempty;
  int *buf1, *buf2, *swapbuf, bufsize; /* for reading lists of integers */
  gen *wbp;
  storage_type st = DENSE;
  read_ident(rfile, kbm_buffer, &delim, FALSE);
  if (delim != '(' || strcmp(kbm_buffer, "rec") != 0) {
    fprintf(stderr, "#Input error: \"rec(\" expected\n");
    exit(1);
  }

  /* main loop reading the fields of the record follows. */
  typeset = FALSE;
  sizeset = FALSE;
  baseset = FALSE;
  arityset = FALSE;
  paddingset = FALSE;
  namesset = FALSE;
  formatset = FALSE;
  labelsset = FALSE;
  alphabetset = FALSE;
  setToLabelsset = FALSE;
  do {
    read_ident(rfile, kbm_buffer, &delim, FALSE);
    if (delim != ':') {
      fprintf(stderr, "#Input error: bad record field assignment\n");
      exit(1);
    }
    check_next_char(rfile, '=');
    if (strcmp(kbm_buffer, "type") == 0) {
      typeset = TRUE;
      read_string(rfile, kbm_buffer, &delim);
      if (strcmp(kbm_buffer, "simple") == 0)
        srptr->type = SIMPLE;
      else if (strcmp(kbm_buffer, "identifiers") == 0)
        srptr->type = IDENTIFIERS;
      else if (strcmp(kbm_buffer, "group generators") == 0) {
        /* put in for compatibility with a variant (by Paul Sanders).
         * Here the "names" field becomes "generatorOrder", and the
         * format field is omitted, with dense format assumed.
         */
        formatset = TRUE;
        st = DENSE;
        srptr->type = IDENTIFIERS;
      }
      else if (strcmp(kbm_buffer, "words") == 0)
        srptr->type = WORDS;
      else if (strcmp(kbm_buffer, "list of words") == 0)
        srptr->type = LISTOFWORDS;
      else if (strcmp(kbm_buffer, "list of integers") == 0)
        srptr->type = LISTOFINTS;
      else if (strcmp(kbm_buffer, "strings") == 0)
        srptr->type = STRINGS;
      else if (strcmp(kbm_buffer, "product") == 0)
        srptr->type = PRODUCT;
      else if (strcmp(kbm_buffer, "labeled") == 0 ||
               strcmp(kbm_buffer, "labelled") == 0)
        srptr->type = LABELED;
      else {
        fprintf(stderr, "Error: unknown set-record type %s.\n", kbm_buffer);
        exit(1);
      }
    }
    else if (strcmp(kbm_buffer, "size") == 0) {
      sizeset = TRUE;
      read_int(rfile, &(srptr->size), &delim);
    }
    else if (strcmp(kbm_buffer, "arity") == 0) {
      arityset = TRUE;
      if (!typeset || srptr->type != PRODUCT) {
        fprintf(stderr, "Error: arity field only used for product type.\n");
        exit(1);
      }
      read_int(rfile, &(srptr->arity), &delim);
    }
    else if (strcmp(kbm_buffer, "padding") == 0) {
      paddingset = TRUE;
      if (!typeset || srptr->type != PRODUCT) {
        fprintf(stderr, "Error: padding field only used for product type.\n");
        exit(1);
      }
      read_ident(rfile, kbm_buffer, &delim, FALSE);
      srptr->padding = kbm_buffer[0];
    }
    else if (strcmp(kbm_buffer, "base") == 0) {
      baseset = TRUE;
      if (!typeset || srptr->type != PRODUCT) {
        fprintf(stderr, "Error: base field only used for product type.\n");
        exit(1);
      }
      tmalloc(srptr->base, srec, 1);
      srec_read(rfile, srptr->base, 0);
      read_delim(rfile, &delim);
    }
    else if (strcmp(kbm_buffer, "format") == 0) {
      formatset = TRUE;
      if (srptr->type != IDENTIFIERS && srptr->type != WORDS &&
          srptr->type != LISTOFWORDS && srptr->type != LISTOFINTS &&
          srptr->type != STRINGS && srptr->type != LABELED) {
        fprintf(stderr,
                "Error: set-record type doesn't require format field.\n");
        exit(1);
      }
      read_string(rfile, kbm_buffer, &delim);
      if (strcmp(kbm_buffer, "dense") == 0)
        st = DENSE;
      else if (strcmp(kbm_buffer, "sparse") == 0)
        st = SPARSE;
      else {
        fprintf(stderr, "Error: unknown storage type %s.\n", kbm_buffer);
        exit(1);
      }
    }
    else if (strcmp(kbm_buffer, "alphabet") == 0) {
      alphabetset = TRUE;
      if (srptr->type != WORDS && srptr->type != LISTOFWORDS) {
        fprintf(stderr,
                "Error: set-record type doesn't require alphabet field.\n");
        exit(1);
      }
      check_next_char(rfile, '[');
      i = 0;
      do {
        read_ident(rfile, kbm_buffer, &delim, TRUE);
        if (stringlen(kbm_buffer) > 0) {
          i++;
          if (i > MAXGEN) {
            fprintf(stderr, "Error: alphabet is too large.\n");
            exit(1);
          }
          tmalloc(srptr->alphabet[i], char, stringlen(kbm_buffer) + 1);
          strcpy(srptr->alphabet[i], kbm_buffer);
        }
      } while (delim != ']');
      srptr->alphabet_size = i;
      check_next_char(rfile, ',');
    }
    else if (strcmp(kbm_buffer, "names") == 0 ||
             strcmp(kbm_buffer, "generatorOrder") == 0) {
      if (!typeset || !sizeset || !formatset) {
        fprintf(stderr,
                "Error: type, size and format fields must precede names.\n");
        exit(1);
      }
      if (srptr->type != IDENTIFIERS && srptr->type != WORDS &&
          srptr->type != STRINGS && srptr->type != LISTOFWORDS &&
          srptr->type != LISTOFINTS) {
        fprintf(stderr,
                "Error: set-record type doesn't require names field.\n");
        exit(1);
      }
      namesset = TRUE;
      check_next_char(rfile, '[');
      if (maxsize < srptr->size)
        maxsize = srptr->size;
      maxsize++; /* hack  (can't remember why) */
      if (srptr->type == LISTOFWORDS)
        tmalloc(srptr->wordslist, gen **,
                maxsize + 1) else if (srptr->type == LISTOFINTS)
            tmalloc(srptr->intlist, int *,
                    maxsize + 1) else if (srptr->type == WORDS)
                tmalloc(srptr->words, gen *, maxsize + 1) else tmalloc(
                    srptr->names, char *, maxsize + 1);
      if (srptr->type == WORDS || srptr->type == LISTOFWORDS) {
        if (!alphabetset) {
          fprintf(stderr, "Error: alphabet field must precede names.\n");
          exit(1);
        }
        /* We now have to work out the algorithm for converting words to
         * generator numbers, based on the alphabet names - similar to that in
         * rwsio.c
         */
        process_names(srptr->alphabet, srptr->alphabet_size);
        /* Finally allocate some space temporarily to read the words into. */
        tmalloc(wbuffer, gen, MAXWORDLEN + 1);
      }
      if (st == DENSE) {
        if (srptr->size == 0)
          read_delim(rfile, &delim);
        for (i = 1; i <= srptr->size; i++) {
          if (srptr->type == IDENTIFIERS) {
            read_ident(rfile, kbm_buffer, &delim, TRUE);
            tmalloc(srptr->names[i], char, stringlen(kbm_buffer) + 1);
            strcpy(srptr->names[i], kbm_buffer);
          }
          else if (srptr->type == STRINGS) {
            read_string(rfile, kbm_buffer, &delim);
            tmalloc(srptr->names[i], char, stringlen(kbm_buffer) + 1);
            strcpy(srptr->names[i], kbm_buffer);
          }
          else if (srptr->type == WORDS) {
            read_word(rfile, wbuffer, wbuffer + MAXWORDLEN, &delim,
                      srptr->alphabet, srptr->alphabet_size, TRUE);
            tmalloc(srptr->words[i], gen, genstrlen(wbuffer) + 1);
            genstrcpy(srptr->words[i], wbuffer);
          }
          else if (srptr->type == LISTOFWORDS) {
            check_next_char(rfile, '[');
            l = 0;
            wbp = wbuffer;
            do {
              nonempty = read_word(rfile, wbp, wbuffer + MAXWORDLEN, &delim,
                                   srptr->alphabet, srptr->alphabet_size, TRUE);
              if (nonempty || l > 0 || delim != ']')
                l++;
              wbp += (genstrlen(wbp) + 1);
            } while (delim != ']');
            tmalloc(srptr->wordslist[i], gen *, l + 1);
            wbp = wbuffer;
            for (j = 0; j < l; j++) {
              tmalloc(srptr->wordslist[i][j], gen, genstrlen(wbp) + 1);
              genstrcpy(srptr->wordslist[i][j], wbp);
              wbp += (genstrlen(wbp) + 1);
            }
            srptr->wordslist[i][l] = 0;
            if (i < srptr->size)
              check_next_char(rfile, ',');
            else
              check_next_char(rfile, ']');
          }
          else if (srptr->type == LISTOFINTS) {
            check_next_char(rfile, '[');
            ct = 0;
            bufsize = 1024;
            tmalloc(buf1, int, bufsize);
            do {
              if (!read_int(rfile, &j, &delim))
                break; /* should only happen if list is empty */
              ct++;
              if (ct >= bufsize) {
                bufsize *= 2;
                tmalloc(buf2, int, bufsize);
                for (k = 1; k < ct; k++)
                  buf2[k] = buf1[k];
                tfree(buf1);
                swapbuf = buf1;
                buf1 = buf2;
                buf2 = swapbuf;
              }
              buf1[ct] = j;
            } while (delim != ']');
            tmalloc(srptr->intlist[i], int, ct + 1);
            for (j = 1; j <= ct; j++)
              srptr->intlist[i][j] = buf1[j];
            srptr->intlist[i][0] = ct;
            if (i < srptr->size)
              check_next_char(rfile, ',');
            else
              check_next_char(rfile, ']');
            tfree(buf1);
          }
        }
      }
      else {
        read_delim(rfile, &delim);
        while (delim != ']') {
          read_int(rfile, &i, &delim);
          if (i <= 0 || i > srptr->size) {
            fprintf(stderr, "Error: name-number out of range.\n");
            exit(1);
          }
          if (srptr->type == IDENTIFIERS) {
            read_ident(rfile, kbm_buffer, &delim, TRUE);
            tmalloc(srptr->names[i], char, stringlen(kbm_buffer) + 1);
            strcpy(srptr->names[i], kbm_buffer);
          }
          else if (srptr->type == STRINGS) {
            read_string(rfile, kbm_buffer, &delim);
            tmalloc(srptr->names[i], char, stringlen(kbm_buffer) + 1);
            strcpy(srptr->names[i], kbm_buffer);
          }
          else if (srptr->type == WORDS) {
            read_word(rfile, wbuffer, wbuffer + MAXWORDLEN, &delim,
                      srptr->alphabet, srptr->alphabet_size, TRUE);
            tmalloc(srptr->words[i], gen, genstrlen(wbuffer) + 1);
            genstrcpy(srptr->words[i], wbuffer);
          }
          else if (srptr->type == LISTOFWORDS) {
            check_next_char(rfile, '[');
            l = 0;
            wbp = wbuffer;
            do {
              nonempty = read_word(rfile, wbp, wbuffer + MAXWORDLEN, &delim,
                                   srptr->alphabet, srptr->alphabet_size, TRUE);
              if (nonempty || l > 0 || delim != ']')
                l++;
              wbp += (genstrlen(wbp) + 1);
            } while (delim != ']');
            tmalloc(srptr->wordslist[i], gen *, l + 1);
            wbp = wbuffer;
            for (j = 0; j < l; j++) {
              tmalloc(srptr->wordslist[i][j], gen, genstrlen(wbp) + 1);
              genstrcpy(srptr->wordslist[i][j], wbp);
              wbp += (genstrlen(wbp) + 1);
            }
            srptr->wordslist[i][l] = 0;
            check_next_char(rfile, ']');
          }
          else if (srptr->type == LISTOFINTS) {
            check_next_char(rfile, '[');
            ct = 0;
            bufsize = 1024;
            tmalloc(buf1, int, bufsize);
            do {
              if (!read_int(rfile, &j, &delim))
                break; /* should only happen if list is empty */
              ct++;
              if (ct >= bufsize) {
                bufsize *= 2;
                tmalloc(buf2, int, bufsize);
                for (k = 1; k < ct; k++)
                  buf2[k] = buf1[k];
                tfree(buf1);
                swapbuf = buf1;
                buf1 = buf2;
                buf2 = swapbuf;
              }
              buf1[ct] = j;
            } while (delim != ']');
            tmalloc(srptr->intlist[i], int, ct + 1);
            for (j = 1; j <= ct; j++)
              srptr->intlist[i][j] = buf1[j];
            srptr->intlist[i][0] = ct;
            check_next_char(rfile, ']');
            tfree(buf1);
          }
          read_delim(rfile, &delim);
          if (delim == ',')
            read_delim(rfile, &delim);
        }
      }
      read_delim(rfile, &delim);
      /* don't forget to de-allocate space for reading words if necessary */
      if (srptr->type == WORDS || srptr->type == LISTOFWORDS)
        tfree(wbuffer);
    }
    else if (strcmp(kbm_buffer, "labels") == 0) {
      labelsset = TRUE;
      if (!typeset || (srptr->type != LABELED)) {
        fprintf(stderr, "Error: labels field only used for labeled type.\n");
        exit(1);
      }
      tmalloc(srptr->labels, srec, 1);
      srec_read(rfile, srptr->labels, 0);
      read_delim(rfile, &delim);
      if (srptr->labels->size > MAXUSHORT) {
        fprintf(stderr, "Error: label-set can have size at most 65535.\n");
        exit(1);
      }
    }
    else if (strcmp(kbm_buffer, "setToLabels") == 0) {
      setToLabelsset = TRUE;
      if (!typeset || (srptr->type != LABELED)) {
        fprintf(stderr,
                "Error: setToLabels field only used for labeled type.\n");
        exit(1);
      }
      if (!labelsset) {
        fprintf(stderr, "Error: labels field must precede setToLabels.\n");
        exit(1);
      }
      if (!formatset) {
        fprintf(stderr, "Error: format field must precede setToLabels.\n");
        exit(1);
      }
      check_next_char(rfile, '[');
      if (maxsize < srptr->size)
        maxsize = srptr->size;
      tmalloc(srptr->setToLabels, setToLabelsType, maxsize + 1);
      for (i = 0; i <= srptr->size; i++)
        srptr->setToLabels[i] = 0;
      if (st == DENSE) {
        for (i = 1; i <= srptr->size; i++) {
          read_int(rfile, &n, &delim);
          if (n < 0 || n > srptr->labels->size) {
            fprintf(stderr, "Error: label out of range.\n");
            exit(1);
          }
          srptr->setToLabels[i] = n;
          if (delim == ']')
            break;
        }
      }
      else {
        read_delim(rfile, &delim);
        while (delim != ']') {
          read_int(rfile, &i, &delim);
          if (i <= 0 || i > srptr->size) {
            fprintf(stderr, "Error: label-number out of range.\n");
            exit(1);
          }
          read_int(rfile, &n, &delim);
          if (n < 0 || n > srptr->labels->size) {
            fprintf(stderr, "Error: label out of range.\n");
            exit(1);
          }
          srptr->setToLabels[i] = n;
          read_delim(rfile, &delim);
          if (delim == ',')
            read_delim(rfile, &delim);
        }
      }
      read_delim(rfile, &delim);
    }
    else {
      printf("#Warning: Unknown field name %s.\n", kbm_buffer);
      skip_gap_expression(rfile, &delim);
    }
  } while (delim != ')');

  if (!typeset || !sizeset) {
    fprintf(stderr, "Error: type and size fields must be set.\n");
    exit(1);
  }
  if (srptr->type == IDENTIFIERS || srptr->type == WORDS ||
      srptr->type == STRINGS || srptr->type == LISTOFWORDS ||
      srptr->type == LISTOFINTS) {
    if (!namesset) {
      fprintf(stderr, "Error: set-record type requires names field.\n");
      exit(1);
    }
  }
  if (srptr->type == PRODUCT) {
    if (!arityset || !paddingset || !baseset) {
      fprintf(
          stderr,
          "Error: set-record type require arity, padding and base fields.\n");
      exit(1);
    }
    /* The size of the set-record should be (n+1)^r or (n+1)^r-1, where
     * n is the size of the base set, and r the arity.
     */
    n = srptr->base->size + 1;
    for (i = 1; i < srptr->arity; i++)
      n *= (srptr->base->size + 1);
    if (srptr->size != n && srptr->size != n - 1) {
      fprintf(stderr, "Error: set-record size wrong for product type.\n");
      exit(1);
    }
  }
  if (srptr->type == LABELED) {
    if (!setToLabelsset) {
      fprintf(stderr, "Error: set-record type require setToLabels field.\n");
      exit(1);
    }
  }
}

/* Read the table_struc *tableptr from rfile, assigning space as required.
 * dr is the number of rows stored densely if storage_type=SPARSE.
 * ns and ne are the sizes of the state-set and alphabet-set.
 * maxstates is the maximum number of states that we allocate space for
 * in dense-storage mode.
 * nondet is true if fsa-table is not known to be deterministic.
 */
void table_read(FILE *rfile, table_struc *tableptr,
                storage_type table_storage_type, int dr, int ns, int maxstates,
                int ne, boolean nondet)
{
  int delim, i, j, k, ntleft = 0, dt = 0;
  boolean filenameset, numTransitionsset, transitionsset;
  int **table, *sparseptr = 0;

  numTransitionsset = FALSE;
  transitionsset = FALSE;
  filenameset = FALSE;

  read_ident(rfile, kbm_buffer, &delim, FALSE);
  if (delim != '(' || strcmp(kbm_buffer, "rec") != 0) {
    fprintf(stderr, "#Input error: \"rec(\" expected\n");
    exit(1);
  }

  /* main loop reading the fields of the record follows. */
  do {
    read_ident(rfile, kbm_buffer, &delim, FALSE);
    if (delim != ':') {
      fprintf(stderr, "#Input error: bad record field assignment\n");
      exit(1);
    }
    check_next_char(rfile, '=');
    if (strcmp(kbm_buffer, "filename") == 0) {
      /* In this case, the transition table (and possibly other info) is stored
       * externally in the file "filename"
       */
      if (transitionsset) {
        fprintf(stderr, "#Input error - filename and transitions fields are "
                        "both present.\n");
        exit(1);
      }
      filenameset = TRUE;
      read_string(rfile, kbm_buffer, &delim);
      tmalloc(tableptr->filename, char, stringlen(kbm_buffer) + 1);
      strcpy(tableptr->filename, kbm_buffer);
    }
    else if (strcmp(kbm_buffer, "format") == 0) {
      read_string(rfile, kbm_buffer, &delim);
      if (strcmp(kbm_buffer, "dense deterministic") == 0)
        tableptr->printing_format = DENSE;
      else if (strcmp(kbm_buffer, "sparse") == 0)
        tableptr->printing_format = SPARSE;
      else {
        fprintf(stderr, "Error: invalid format value %s\n", kbm_buffer);
        exit(1);
      }
    }
    else if (strcmp(kbm_buffer, "numTransitions") == 0) {
      numTransitionsset = TRUE;
      read_int(rfile, &(tableptr->numTransitions), &delim);
    }
    else if (strcmp(kbm_buffer, "defaultTarget") == 0) {
      read_int(rfile, &dt, &delim);
      if (dt != 0)
        /* Our code doesn't currently cope with this, so we will enforce
         * storage-type dense in this case.
         */
        table_storage_type = DENSE;
    }
    else if (strcmp(kbm_buffer, "transitions") == 0) {
      if (filenameset) {
        fprintf(stderr, "#Input error - filename and transitions fields are "
                        "both present.\n");
        exit(1);
      }
      transitionsset = TRUE;
      if (!numTransitionsset) {
        /* In this case, we have currently to use dense storage */
        if (nondet) {
          fprintf(stderr, "Error: In a non-deterministic trnasition table\n");
          fprintf(stderr, "you must include the 'numTransitions' field,\n");
          exit(1);
        }
        table_storage_type = DENSE;
      }
      tableptr->table_type = table_storage_type;
      if (table_storage_type == SPARSE) {
        if (dr > ns)
          dr = ns;
        tableptr->denserows = dr;
      }
      tableptr->maxstates = maxstates;
      /*  assign space according to storage type */
      if (table_storage_type == DENSE) {
        fsa_table_init(tableptr, maxstates, ne);
        table = tableptr->table_data_ptr;
      }
      else {
        tmalloc(tableptr->table_data_ptr, int *, ns + 2);
        table = tableptr->table_data_ptr;
        if (dr > 0) {
          /* Here we have to read in the data in two separate chunks. */
          tmalloc(table[0], int, dr *ne);
          for (i = 1; i <= dr; i++)
            table[i] = table[0] + (i - 1) * ne - 1;
          ntleft = tableptr->numTransitions;
        }
        else {
          tmalloc(table[0], int, 2 * tableptr->numTransitions + 1);
          sparseptr = table[0];
        }
      }
      /* Now read the transitions  */
      check_next_char(rfile, '[');
      for (i = 1; i <= ns; i++) {
        check_next_char(rfile, '[');
        if (table_storage_type == SPARSE) {
          if (dr > 0 && i == dr + 1) { /* allocate remaining storage */
            tmalloc(table[i], int, 2 * ntleft + 1);
            sparseptr = table[i];
          }
          else if (i > dr)
            table[i] = sparseptr;
        }
        if (tableptr->printing_format == DENSE) {
          if (ne == 0)
            read_delim(rfile, &delim);
          else
            for (j = 1; j <= ne; j++) {
              read_int(rfile, &k, &delim);
              if (table_storage_type == DENSE)
                table[j][i] = k;
              else if (i <= dr) {
                table[i][j] = k;
                if (k != 0)
                  ntleft--;
              }
              else if (k != 0) {
                *(sparseptr++) = j;
                *(sparseptr++) = k;
              }
              if (k > ns) {
                fprintf(stderr,
                        "Error: transition target out of range in table.\n");
                exit(1);
              }
            }
          if (delim != ']') {
            fprintf(stderr, "Error: format error in table.\n");
            exit(1);
          }
        }
        else {
          if (table_storage_type == DENSE)
            for (j = 1; j <= ne; j++)
              table[j][i] = dt; /* in case there is a default target */
          else if (i <= dr)
            for (j = 1; j <= ne; j++)
              table[i][j] = 0;
          read_delim(rfile, &delim);
          while (delim != ']') {
            read_int(rfile, &j, &delim);
            read_int(rfile, &k, &delim);
            if (table_storage_type == DENSE)
              table[j][i] = k;
            else if (i <= dr) {
              table[i][j] = k;
              if (k != 0)
                ntleft--;
            }
            else if (k != 0) {
              *(sparseptr++) = j;
              *(sparseptr++) = k;
            }
            if (k > ns) {
              fprintf(stderr,
                      "Error: transition target out of range in table.\n");
              exit(1);
            }
            read_delim(rfile, &delim);
            if (delim == ',')
              read_delim(rfile, &delim);
          }
        }
        read_delim(rfile, &delim);
      }
      if (ns == 0)
        read_delim(rfile, &delim);
      if (table_storage_type == SPARSE && (ns > dr || ns == 0))
        /* Set extra pointer to show end of data */
        table[ns + 1] = sparseptr;
      read_delim(rfile, &delim);
    }
    else {
      printf("#Warning: Unknown field name %s.\n", kbm_buffer);
      skip_gap_expression(rfile, &delim);
    }
  } while (delim != ')');

  if (!transitionsset && !filenameset) {
    fprintf(stderr, "transitions and filename fields are not set.\n");
    exit(1);
  }
  if (filenameset)
    /* In this case, the storage_type has to be as specified by the format field
     */
    tableptr->table_type = tableptr->printing_format;
}

/* Read the fsa *fsaptr from rfile, assigning space as required.
 * If maxstates is greater than the actual number of states, then
 * space will be assigned for maxstates states, to allow more states
 * to be added later (this only makes sense with dense storage_type).
 * If assignment is true, an assignment to an fsa is read.
 * The name is returned in *name, which is assumed to have enough space.
 */
void fsa_read(FILE *rfile, fsa *fsaptr, storage_type table_storage_type, int dr,
              int maxstates, boolean assignment, char *name)
{
  int delim, i, j, k, ct, ns = 0, ne = 0, *ia, *buf1, *buf2, *swapbuf, bufsize;
  boolean isFSAset, statesset, alphabetset, initialset, acceptingset, flagsset,
      tableset, in, compressed;

  if (kbm_print_level >= 3)
    printf("    #Calling fsa_read.\n");
  isFSAset = FALSE;
  isFSAset = FALSE;
  statesset = FALSE;
  alphabetset = FALSE;
  initialset = FALSE;
  acceptingset = FALSE;
  flagsset = FALSE;
  tableset = FALSE;

  fsa_init(fsaptr);

  if (assignment) {
    read_ident(rfile, kbm_buffer, &delim, FALSE);
    if (delim != ':') {
      fprintf(stderr, "#Input error: file must contain a record assignment\n");
      exit(1);
    }
    strcpy(name, kbm_buffer);
    check_next_char(rfile, '=');
  }

  read_ident(rfile, kbm_buffer, &delim, FALSE);
  if (delim != '(' || strcmp(kbm_buffer, "rec") != 0) {
    fprintf(stderr, "#Input error: \"rec(\" expected\n");
    exit(1);
  }

  /* main loop reading the fields of the record follows. */
  do {
    read_ident(rfile, kbm_buffer, &delim, FALSE);
    if (delim != ':') {
      fprintf(stderr, "#Input error: bad record field assignment\n");
      exit(1);
    }
    check_next_char(rfile, '=');
    if (strcmp(kbm_buffer, "isFSA") == 0) {
      isFSAset = TRUE;
      read_ident(rfile, kbm_buffer, &delim, FALSE);
      if (strcmp(kbm_buffer, "true") != 0) {
        fprintf(stderr, "#Input error: isFSA field must equal \"true\"\n");
        exit(1);
      }
    }
    else if (strcmp(kbm_buffer, "states") == 0) {
      statesset = TRUE;
      srec_read(rfile, fsaptr->states, maxstates);
      ns = fsaptr->states->size;
      read_delim(rfile, &delim);
    }
    else if (strcmp(kbm_buffer, "alphabet") == 0) {
      alphabetset = TRUE;
      srec_read(rfile, fsaptr->alphabet, 0);
      ne = fsaptr->alphabet->size;
      read_delim(rfile, &delim);
    }
    else if (strcmp(kbm_buffer, "flags") == 0) {
      flagsset = TRUE;
      check_next_char(rfile, '[');
      do {
        read_string(rfile, kbm_buffer, &delim);
        for (i = 0; i < num_kbm_flag_strings; i++)
          if (strcmp(kbm_buffer, kbm_flag_names[i]) == 0)
            fsaptr->flags[(kbm_flag_strings)i] = TRUE;
      } while (delim != ']');
      read_delim(rfile, &delim);
      /* If the fsa is not known to be deterministic (or (25.7.95.) possibly
       * multiple start-state deterministic), then we should store is in
       * sparse format
       */
      if (!fsaptr->flags[DFA] && !fsaptr->flags[MIDFA]) {
        table_storage_type = SPARSE;
        dr = 0;
      }
    }
    else if (strcmp(kbm_buffer, "initial") == 0 ||
             strcmp(kbm_buffer, "accepting") == 0) {
      /* We'll handle these together since they are so similar. */
      compressed = FALSE;
      bufsize = 1024;
      in = strcmp(kbm_buffer, "initial") == 0;
      if (!statesset) {
        fprintf(
            stderr,
            "Error: states field must precede initial and accepting fields.\n");
        exit(1);
      }
      ct = 0;
      tmalloc(buf1, int, bufsize);
      /* We don't know how long the list will be in advance */
      check_next_char(rfile, '[');
      do {
        read_int(rfile, &i, &delim);
        if (i < 0 || i > ns) {
          fprintf(stderr,
                  "Invalid state number in initial or accepting list.\n");
          exit(1);
        }
        if (i > 0) {
          ct++;
          if (delim == '.') {
            compressed = TRUE;
            if (ct != 1) {
              fprintf(stderr,
                      "Error in format of initial or accepting field.\n");
              exit(1);
            }
            check_next_char(rfile, '.');
            read_int(rfile, &j, &delim);
            if (j >= i && j <= ns) {
              if (delim != ']') {
                fprintf(stderr,
                        "Error in format of initial or accepting field.\n");
                exit(1);
              }
              ct = j - i + 1;
              if (ct == 1 || ct != ns || in) {
                /* if ct = ns, we don't store the list of accept states. */
                tmalloc(ia, int, ct + 1);
                if (in)
                  fsaptr->initial = ia;
                else
                  fsaptr->accepting = ia;
                for (k = 1; k <= ct; k++)
                  ia[k] = k + i - 1;
              }
            }
            else {
              fprintf(stderr,
                      "Error in format of initial or accepting field.\n");
              exit(1);
            }
          }
          else {
            compressed = FALSE;
            if (ct >= bufsize) {
              bufsize *= 2;
              tmalloc(buf2, int, bufsize);
              for (j = 1; j < ct; j++)
                buf2[j] = buf1[j];
              tfree(buf1);
              swapbuf = buf1;
              buf1 = buf2;
              buf2 = swapbuf;
            }
            buf1[ct] = i;
          }
        } /* if (i>0) */
      } while (delim != ']');
      if (!compressed) {
        tmalloc(ia, int, ct + 1);
        if (in)
          fsaptr->initial = ia;
        else
          fsaptr->accepting = ia;
        for (i = 1; i <= ct; i++)
          ia[i] = buf1[i];
      }
      tfree(buf1);
      if (in) {
        initialset = TRUE;
        fsaptr->num_initial = ct;
      }
      else {
        acceptingset = TRUE;
        fsaptr->num_accepting = ct;
      }
      read_delim(rfile, &delim);
    }
    else if (strcmp(kbm_buffer, "table") == 0) {
      if (!statesset) {
        fprintf(stderr, "Error: states field must precede table fields.\n");
        exit(1);
      }
      if (!alphabetset) {
        fprintf(stderr, "Error: alphabet field must precede table fields.\n");
        exit(1);
      }
      tableset = TRUE;
      if (fsaptr->alphabet->type == PRODUCT)
        k = fsaptr->alphabet->base->size;
      else
        k = -1;
      if (maxstates < ns)
        maxstates = ns;
      if (!fsaptr->flags[DFA] && !fsaptr->flags[MIDFA])
        table_read(rfile, fsaptr->table, table_storage_type, dr, ns, maxstates,
                   ne, TRUE);
      else
        table_read(rfile, fsaptr->table, table_storage_type, dr, ns, maxstates,
                   ne, FALSE);
      read_delim(rfile, &delim);
    }
    else {
      printf("#Warning: Unknown field name %s.\n", kbm_buffer);
      skip_gap_expression(rfile, &delim);
    }
  } while (delim != ')');

  if (!isFSAset || !statesset || !alphabetset || !initialset || !acceptingset ||
      !flagsset || !tableset) {
    fprintf(stderr, "One of the compulsory fsa-fields is not set.\n");
    exit(1);
  }
  if (assignment)
    read_delim(rfile, &delim);
}

/* Read the transition-table of the fsa *fsaptr which is stored in the
 * file with *file in unformatted form.
 * *file should be opened and closed externally to the function.
 * This is used by the functions in fsalogic.c
 */
void compressed_transitions_read(fsa *fsaptr, FILE *rfile)
{
  int ns, ne, nt, **table, *tab_ptr, len, i, j;

  ns = fsaptr->states->size;
  ne = fsaptr->alphabet->size;
  nt = fsaptr->table->numTransitions;

  if (fsaptr->table->table_type == DENSE) {
    /* it is a pity that the table was output in transposed form! */
    fsa_table_init(fsaptr->table, ns, ne);
    table = fsaptr->table->table_data_ptr;
    for (i = 1; i <= ns; i++)
      for (j = 1; j <= ne; j++) {
        size_t s = fread((void *)(table[j] + i), sizeof(int), (size_t)1, rfile);
        (void)s; // HACK to silence compiler warning
      }
  }
  else {
    tmalloc(fsaptr->table->table_data_ptr, int *, ns + 2);
    tmalloc(fsaptr->table->table_data_ptr[0], int, 2 * nt + 1);
    tab_ptr = fsaptr->table->table_data_ptr[0];
    table = fsaptr->table->table_data_ptr;
    for (i = 1; i <= ns; i++) {
      table[i] = tab_ptr;
      fread((void *)&len, sizeof(int), (size_t)1, rfile);
      if (len > 0)
        fread((void *)tab_ptr, sizeof(int), (size_t)len, rfile);
      tab_ptr += len;
    }
    table[ns + 1] = tab_ptr;
  }
}
