/* $Id: lpgutil.c,v 1.2 1999/11/04 14:02:22 shields Exp $ */
/*
 This software is subject to the terms of the IBM Jikes Compiler
 License Agreement available at the following URL:
 http://www.ibm.com/research/jikes.
 Copyright (C) 1983, 1999, International Business Machines Corporation
 and others.  All Rights Reserved.
 You must accept the terms of that agreement to use this software.
*/
static char hostfile[] = __FILE__;

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "common.h"
#include "header.h"

/**********************************************************************/
/* The following are global variables and constants used to manage a  */
/* pool of temporary space. Externally, the user invokes the function */
/* "talloc" just as he would invoke "malloc".                         */
/**********************************************************************/
#ifdef DOS
#define LOG_BLKSIZE 12
#else
#define LOG_BLKSIZE 14
#endif

#define BLKSIZE (1 << LOG_BLKSIZE)
#define BASE_INCREMENT 64

typedef long cell;

static cell **temp_base = NULL;
static long temp_top = 0,
            temp_size = 0,
            temp_base_size = 0;

/**********************************************************************/
/*                          ALLOCATE_MORE_SPACE:                      */
/**********************************************************************/
/* This procedure obtains more TEMPORARY space.                       */
/**********************************************************************/
static BOOLEAN allocate_more_space(cell ***base, long *size, long *base_size)
{
    int k;

/**********************************************************************/
/* The variable size always indicates the maximum number of cells     */
/* that has been allocated and reserved for the storage pool.         */
/* Initially, size should be set to 0 to indicate that no space has   */
/* yet been allocated. The pool of cells available is divided into    */
/* segments of size 2**LOG_BLKSIZE each and each segment is pointer   */
/* to by a slot in the array base.                                    */
/*                                                                    */
/* By dividing "size" by the size of the segment we obtain the        */
/* index for the next segment in base. If base is already full, it is */
/* reallocated.                                                       */
/*                                                                    */
/**********************************************************************/
    k = (*size) >> LOG_BLKSIZE; /* which segment? */
    if (k == (*base_size))      /* base overflow? reallocate */
    {
        register int i = (*base_size);

        (*base_size) += BASE_INCREMENT;
        (*base) = (cell **)
                  ((*base) == NULL ?
                   malloc(sizeof(cell *) * (*base_size)) :
                   realloc((*base), sizeof(cell *) * (*base_size)));
        if ((*base) == (cell **) NULL)
            return FALSE;

        for (i = i; i < (*base_size); i++)
            (*base)[i] = NULL;
    }

/**********************************************************************/
/* If the Ast slot "k" does not already contain a segment, We try to  */
/* allocate one and place its address in (*base)[k].                  */
/* If the allocation was not successful, we terminate;                */
/* otherwise, we adjust the address in (*base)[k] so as to allow us   */
/* to index the segment directly, instead of having to perform a      */
/* subtraction for each reference. Finally, we update size.           */
/*                                                                    */
/* Finally, we set the block to zeros.                                */
/**********************************************************************/
    if ((*base)[k] == NULL)
    {
        (*base)[k] = (cell *) malloc(sizeof(cell) << LOG_BLKSIZE);
        if ((*base)[k] == (cell *) NULL)
            return FALSE;
        (*base)[k] -= (*size);
    }

    memset((void *)((*base)[k] + (*size)), 0, sizeof(cell) << LOG_BLKSIZE);
    (*size) += BLKSIZE;

    return TRUE;
}


/**********************************************************************/
/*                         RESET_TEMPORARY_SPACE:                     */
/**********************************************************************/
/* This procedure resets the temporary space already allocated so     */
/* that it can be reused before new blocks are allocated.             */
/**********************************************************************/
void reset_temporary_space(void)
{
    temp_top = 0;         /* index of next usable elemt */
    temp_size = 0;

    return;
}


/**********************************************************************/
/*                         FREE_TEMPORARY_SPACE:                      */
/**********************************************************************/
/* This procedure frees all allocated temporary space.                */
/**********************************************************************/
void free_temporary_space(void)
{
    int k;

    for (k = 0; k < temp_base_size && temp_base[k] != NULL; k++)
    {
        temp_base[k] += (k * BLKSIZE);
        ffree(temp_base[k]);
    }

    if (temp_base != NULL)
    {
        ffree(temp_base);
        temp_base = NULL;
    }

    temp_base_size = 0;
    temp_top = 0;
    temp_size = 0;

    return;
}


/**********************************************************************/
/*                                TALLOC:                             */
/**********************************************************************/
/* talloc allocates an object of size "size" in temporary space and   */
/* returns a pointer to it.                                           */
/**********************************************************************/
void *talloc(long size)
{
    long i;

    i = temp_top;
    temp_top += ((size + sizeof(cell) - 1) / sizeof(cell));
    if (temp_top > temp_size)
    {
        i = temp_size;
        temp_top = temp_size +
                   ((size + sizeof(cell) - 1) / sizeof(cell));
        if (! allocate_more_space(&temp_base, &temp_size, &temp_base_size))
        {
            temp_top = temp_size;
            return NULL;
        }
    }

    return ((void *) &(temp_base[i >> LOG_BLKSIZE] [i]));
}


/**********************************************************************/
/*                      TEMPORARY_SPACE_ALLOCATED:                    */
/**********************************************************************/
/* Return the total size of temporary space allocated.                */
/**********************************************************************/
long temporary_space_allocated(void)
{
    return ((temp_base_size * sizeof(cell **)) +
            (temp_size * sizeof(cell)));
}


/**********************************************************************/
/*                         TEMPORARY_SPACE_USED:                      */
/**********************************************************************/
/* Return the total size of temporary space used.                     */
/**********************************************************************/
long temporary_space_used(void)
{
    return (((temp_size >> LOG_BLKSIZE) * sizeof(cell **)) +
             (temp_top * sizeof(cell)));
}


/**********************************************************************/
/*                                                                    */
/* The following are global variables and constants used to manage a  */
/* pool of global space. Externally, the user invokes one of the      */
/* functions:                                                         */
/*                                                                    */
/*    ALLOCATE_NODE                                                   */
/*    ALLOCATE_GOTO_MAP                                               */
/*    ALLOCATE_SHIFT_MAP                                              */
/*    ALLOCATE_REDUCE_MAP                                             */
/*                                                                    */
/* These functions allocate space from the global pool in the same    */
/* using the function "galloc" below.                                 */
/*                                                                    */
/**********************************************************************/
static cell **global_base = NULL;
static long global_top = 0,
            global_size = 0,
            global_base_size = 0;

static struct node *node_pool = NULL;

/**********************************************************************/
/*                          PROCESS_GLOBAL_WASTE:                     */
/**********************************************************************/
/* This function is invoked when the space left in a segment is not   */
/* enough for GALLOC to allocate a requested object. Rather than      */
/* waste the space, as many NODE structures as possible are allocated */
/* in that space and stacked up in the NODE_POOL list.                */
/**********************************************************************/
static void process_global_waste(long top)
{
    struct node *p;
    long i;

    while (TRUE)
    {
        i = top;
        top += ((sizeof(struct node) + sizeof(cell) - 1) / sizeof(cell));
        if (top > global_size)
            break;
        p = (struct node *) &(global_base[i >> LOG_BLKSIZE] [i]);
        p -> next = node_pool;
        node_pool = p;
    }

    return;
}

/**********************************************************************/
/*                                GALLOC:                             */
/**********************************************************************/
/* galloc allocates an object of size "size" in global space and      */
/* returns a pointer to it. It is analoguous to "talloc", but it      */
/* is a local (static) routine that is only invoked in this file by   */
/* other more specialized routines.                                   */
/**********************************************************************/
static void *galloc(long size)
{
    long i;

    i = global_top;
    global_top += ((size + sizeof(cell) - 1) / sizeof(cell));
    if (global_top > global_size)
    {
        process_global_waste(i);
        i = global_size;
        global_top = global_size +
                     ((size + sizeof(cell) - 1) / sizeof(cell));
        if (! allocate_more_space(&global_base,
                                  &global_size, &global_base_size))
        {
            global_top = global_size;
            return NULL;
        }
    }

    return ((void *) &(global_base[i >> LOG_BLKSIZE] [i]));
}


/****************************************************************************/
/*                              ALLOCATE_NODE:                              */
/****************************************************************************/
/*   This function allocates a node structure and returns a pointer to it.  */
/* it there are nodes in the free pool, one of them is returned. Otherwise, */
/* a new node is allocated from the global storage pool.                    */
/****************************************************************************/
struct node *allocate_node(char *file, long line)
{
    struct node *p;

    p = node_pool;
    if (p != NULL)  /* is free list not empty? */
         node_pool = p -> next;
    else
    {
        p = (struct node *) galloc(sizeof(struct node));
        if (p == NULL)
            nospace(file, line);
    }

    return(p);
}


/****************************************************************************/
/*                             FREE_NODES:                                  */
/****************************************************************************/
/*  This function frees a linked list of nodes by adding them to the free   */
/* list.  Head points to head of linked list and tail to the end.           */
/****************************************************************************/
void free_nodes(struct node *head, struct node *tail)
{
    tail -> next = node_pool;
    node_pool = head;

    return;
}


/****************************************************************************/
/*                             ALLOCATE_GOTO_MAP:                           */
/****************************************************************************/
/*   This function allocates space for a goto map with "size" elements,     */
/* initializes and returns a goto header for that map. NOTE that after the  */
/* map is successfully allocated, it is offset by one element. This is      */
/* to allow the array in question to be indexed from 1..size instead of     */
/* 0..(size-1).                                                             */
/****************************************************************************/
struct goto_header_type allocate_goto_map(int size, char *file, long line)
{
    struct goto_header_type go_to;

    go_to.size = size;
    go_to.map = (struct goto_type *)
                galloc(size * sizeof(struct goto_type));
    if (go_to.map == NULL)
        nospace(file, line);
    go_to.map--;   /* map will be indexed in range 1..size */

    return(go_to);
}


/****************************************************************************/
/*                            ALLOCATE_SHIFT_MAP:                           */
/****************************************************************************/
/*   This function allocates space for a shift map with "size" elements,    */
/* initializes and returns a shift header for that map. NOTE that after the */
/* map is successfully allocated, it is offset by one element. This is      */
/* to allow the array in question to be indexed from 1..size instead of     */
/* 0..(size-1).                                                             */
/****************************************************************************/
struct shift_header_type allocate_shift_map(int size,
                                            char *file, long line)
{
    struct shift_header_type sh;

    sh.size = size;
    sh.map = (struct shift_type *)
             galloc(size * sizeof(struct shift_type));
    if (sh.map == NULL)
        nospace(file, line);
    sh.map--;   /* map will be indexed in range 1..size */

    return(sh);
}


/****************************************************************************/
/*                             ALLOCATE_REDUCE_MAP:                         */
/****************************************************************************/
/*   This function allocates space for a REDUCE map with "size"+1 elements, */
/* initializes and returns a REDUCE header for that map. The 0th element of */
/* a reduce map is used for the default reduction.                          */
/****************************************************************************/
struct reduce_header_type allocate_reduce_map(int size,
                                              char *file, long line)
{
    struct reduce_header_type red;

    red.map = (struct reduce_type *)
              galloc((size + 1) * sizeof(struct reduce_type));
    if (red.map == NULL)
        nospace(file, line);
    red.size = size;

    return(red);
}


/**********************************************************************/
/*                        GLOBAL_SPACE_ALLOCATED:                     */
/**********************************************************************/
/* Return the total size of global space allocated.                   */
/**********************************************************************/
long global_space_allocated(void)
{
    return ((global_base_size * sizeof(cell **)) +
            (global_size * sizeof(cell)));
}


/**********************************************************************/
/*                           GLOBAL_SPACE_USED:                       */
/**********************************************************************/
/* Return the total size of global space used.                        */
/**********************************************************************/
long global_space_used(void)
{
    return (((global_size >> LOG_BLKSIZE) * sizeof(cell **)) +
             (global_top * sizeof(cell)));
}


/****************************************************************************/
/*                           ALLOCATE_INT_ARRAY:                            */
/****************************************************************************/
/*   This function allocates an array of size "size" of int integers.       */
/****************************************************************************/
int *allocate_int_array(long size, char *file, long line)
{
    int *p;

    p = (int *) calloc(size, sizeof(int));
    if (p == (int *) NULL)
        nospace(file, line);

    return(&p[0]);
}


/****************************************************************************/
/*                           ALLOCATE_SHORT_ARRAY:                          */
/****************************************************************************/
/*   This function allocates an array of size "size" of short integers.     */
/****************************************************************************/
short *allocate_short_array(long size, char *file, long line)
{
    short *p;

    p = (short *) calloc(size, sizeof(short));
    if (p == (short *) NULL)
        nospace(file, line);

    return(&p[0]);
}


/****************************************************************************/
/*                           ALLOCATE_BOOLEAN_ARRAY:                        */
/****************************************************************************/
/*   This function allocates an array of size "size" of type boolean.       */
/****************************************************************************/
BOOLEAN *allocate_boolean_array(long size, char *file, long line)
{
    BOOLEAN *p;

    p = (BOOLEAN *) calloc(size, sizeof(BOOLEAN));
    if (p == (BOOLEAN *) 0)
        nospace(file, line);

    return(&p[0]);
}


/*****************************************************************************/
/*                              FILL_IN:                                     */
/*****************************************************************************/
/* FILL_IN is a subroutine that pads a buffer, STRING,  with CHARACTER a     */
/* certain AMOUNT of times.                                                  */
/*****************************************************************************/
void fill_in(char string[], int amount, char character)
{
    int i;

    for (i = 0; i <= amount; i++)
        string[i] = character;
    string[i] = '\0';

    return;
}


/*****************************************************************************/
/*                                  QCKSRT:                                  */
/*****************************************************************************/
/* QCKSRT is a quicksort algorithm that takes as arguments an array of       */
/* integers, two numbers L and H that indicate the lower and upper bound     */
/* positions in ARRAY to be sorted.                                          */
/*****************************************************************************/
static void qcksrt(short array[], int l, int h)
{
    int lower,
        upper,
        top,
        i,
        j,
        pivot,
        lostack[14],  /* A stack of size 14 can sort an array of up to */
        histack[14];  /* 2 ** 15 - 1 elements                          */

    top = 1;
    lostack[top] = l;
    histack[top] = h;
    while (top != 0)
    {
        lower = lostack[top];
        upper = histack[top--];

        while (upper > lower)
        {
            i = lower;
            pivot = array[lower];
            for (j = lower + 1; j <= upper; j++)
            {
                if (array[j] < pivot)
                {
                    array[i] = array[j];
                    i++;
                    array[j] = array[i];
                }
            }
            array[i] = pivot;

            top++;
            if (i - lower < upper - i)
            {
                lostack[top] = i + 1;
                histack[top] = upper;
                upper = i - 1;
            }
            else
            {
                histack[top] = i - 1;
                lostack[top] = lower;
                lower = i + 1;
            }
        }
    }

    return;
}


/*****************************************************************************/
/*                               NUMBER_LEN:                                 */
/*****************************************************************************/
/* NUMBER_LEN takes a state number and returns the number of digits in that  */
/* number.                                                                   */
/*****************************************************************************/
int number_len(int state_no)
{
    int num = 0;

    do
    {
        state_no /= 10;
        num++;
    }   while (state_no != 0);

    return num;
}


/*************************************************************************/
/*                            RESTORE_SYMBOL:                            */
/*************************************************************************/
/* This procedure takes two character strings as arguments: IN and OUT.  */
/* IN identifies a grammar symbol or name that is checked as to whether  */
/* or not it needs to be quoted. If so, the necessary quotes are added   */
/* as IN is copied into the space identified by OUT.                     */
/* NOTE that it is assumed that IN and OUT do not overlap each other.    */
/*************************************************************************/
void restore_symbol(char *out, char *in)
{
    int  len;

    len = strlen(in);
    if (len > 0)
    {
        if ((len == 1 && in[0] == ormark) ||
            (in[0] == escape)             ||
            (in[0] == '\'')               ||
            (in[len - 1] == '\'')         ||
            (strchr(in, ' ') != NULL &&
            (in[0] != '<' || in[len - 1] != '>')))
        {
            *(out++) = '\'';
            while(*in != '\0')
            {
                if (*in == '\'')
                    *(out++) = *in;
                *(out++) = *(in++);
            }
            *(out++) = '\'';
            *out = '\0';

            return;
        }
    }

    strcpy(out, in);

    if (out[0] == '\n')   /* one of the special grammar symbols? */
        out[0] = escape;

    return;
}


/*****************************************************************************/
/*                          PRINT_LARGE_TOKEN:                               */
/*****************************************************************************/
/* PRINT_LARGE_TOKEN generates code to print a token that may exceed the     */
/* limit of its field.  The argument are LINE which is the symbol a varying  */
/* length character string, TOKEN which is the symbol to be printed, INDENT  */
/* which is a character string to be used as an initial prefix to indent the */
/* output line, and LEN which indicates the maximum number of characters that*/
/* can be printed on a given line.  At the end of this process, LINE will    */
/* have the value of the remaining substring that can fit on the output line.*/
/* If a TOKEN is too large to be indented in a line, but not too large for   */
/* the whole line, we forget the indentation, and printed it. Otherwise, it  */
/* is "chapped up" and printed in pieces that are each indented.             */
/*****************************************************************************/
void print_large_token(char *line, char *token, char *indent, int len)
{
    int toklen;

    char temp[SYMBOL_SIZE + 1];

    toklen = strlen(token);

    if (toklen > len && toklen <= PRINT_LINE_SIZE-1)
    {
        fprintf(syslis, "\n%s", token);
        ENDPAGE_CHECK;
        token = "";
        strcpy(line,indent);
    }
    else
    {
        for (; toklen > len; toklen = strlen(temp))
        {
            memcpy(temp, token, len);
            temp[len] = '\0';
            fprintf(syslis, "\n%s",temp);
            ENDPAGE_CHECK;
            strcpy(temp, token+len + 1);
            token = temp;
        }
        strcpy(line,indent);
        strcat(line,token);
    }

    return;
}


/*****************************************************************************/
/*                                PRINT_ITEM:                                */
/*****************************************************************************/
/* PRINT_ITEM takes as parameter an ITEM_NO which it prints.                 */
/*****************************************************************************/
void print_item(int item_no)
{
    int rule_no,
        symbol,
        len,
        offset,
        i,
        k;

    char tempstr[PRINT_LINE_SIZE + 1],
         line[PRINT_LINE_SIZE + 1],
         tok[SYMBOL_SIZE + 1];

    /*********************************************************************/
    /* We first print the left hand side of the rule, leaving at least   */
    /* 5 spaces in the output line to accomodate the equivalence symbol  */
    /* "::=" surrounded by blanks on both sides.  Then, we print all the */
    /* terminal symbols in the right hand side up to but not including   */
    /* the dot symbol.                                                   */
    /*********************************************************************/

    rule_no = item_table[item_no].rule_number;
    symbol = rules[rule_no].lhs;

    restore_symbol(tok, RETRIEVE_STRING(symbol));
    len = PRINT_LINE_SIZE - 5;
    print_large_token(line, tok, "", len);
    strcat(line, " ::= ");
    i = (PRINT_LINE_SIZE / 2) - 1;
    offset = MIN(strlen(line)-1, i);
    len = PRINT_LINE_SIZE - (offset + 4);
    i = rules[rule_no].rhs;  /* symbols before dot */

    k = ((rules[rule_no].rhs + item_table[item_no].dot) - 1);
    for (; i <= k; i++)
    {
        symbol = rhs_sym[i];
        restore_symbol(tok, RETRIEVE_STRING(symbol));
        if (strlen(tok) + strlen(line) > PRINT_LINE_SIZE - 4)
        {
            fprintf(syslis,"\n%s", line);
            ENDPAGE_CHECK;
            fill_in(tempstr, offset, SPACE);
            print_large_token(line, tok, tempstr, len);
        }
        else
            strcat(line, tok);
        strcat(line, BLANK);
    }

    /*********************************************************************/
    /* We now add a DOT "." to the output line and print the remaining   */
    /* symbols in the right hand side.  If ITEM_NO is a complete item,   */
    /* we also print the rule number.                                    */
    /*********************************************************************/
    if (item_table[item_no].dot == 0 || item_table[item_no].symbol == empty)
        strcpy(tok, ".");
    else
        strcpy(tok, " .");
    strcat(line, tok);
    len = PRINT_LINE_SIZE - (offset + 1);
    for (i = rules[rule_no].rhs +
              item_table[item_no].dot;/* symbols after dot*/
          i <= rules[rule_no + 1].rhs - 1; i++)
    {
        symbol = rhs_sym[i];
        restore_symbol(tok, RETRIEVE_STRING(symbol));
        if (strlen(tok) + strlen(line) > PRINT_LINE_SIZE -1)
        {
            fprintf(syslis, "\n%s", line);
            ENDPAGE_CHECK;
            fill_in(tempstr, offset, SPACE);
            print_large_token(line, tok, tempstr, len);
        }
        else
            strcat(line, tok);
        strcat(line, BLANK);
    }
    if (item_table[item_no].symbol == empty)   /* complete item */
    {
        sprintf(tok, " (%d)", rule_no);
        if (strlen(tok) + strlen(line) > PRINT_LINE_SIZE - 1)
        {
            fprintf(syslis, "\n%s", line);
            ENDPAGE_CHECK;
            fill_in(line,offset, SPACE);
        }
        strcat(line,tok);
    }
    fprintf(syslis, "\n%s", line);
    ENDPAGE_CHECK;

    return;
}


/*****************************************************************************/
/*                               PRINT_STATE:                                */
/*****************************************************************************/
/* PRINT_STATE prints all the items in a state.  NOTE that when single       */
/* productions are eliminated, certain items that were added in a state by   */
/* CLOSURE, will no longer show up in the output.  Example: If we have the   */
/* item [A ::= .B]  in a state, and the GOTO_REDUCE generated on B has been  */
/* replaced by say the GOTO or GOTO_REDUCE of A, the item above can no longer*/
/* be retrieved, since transitions in a given state are reconstructed from   */
/* the KERNEL and ADEQUATE items of the actions in the GOTO and SHIFT maps.  */
/*****************************************************************************/
void print_state(int state_no)
{
    struct shift_header_type sh;

    struct goto_header_type  go_to;

    short *item_list;

    int kernel_size,
        i,
        n,
        item_no,
        next_state;

    BOOLEAN end_node,
            *state_seen,
            *item_seen;

    struct node *q;

    char buffer[PRINT_LINE_SIZE + 1],
         line[PRINT_LINE_SIZE + 1];

    /*********************************************************************/
    /* ITEM_SEEN is used to construct sets of items, to help avoid       */
    /* adding duplicates in a list.  Duplicates can occur because an     */
    /* item from the kernel set will either be shifted on if it is not a */
    /* complete item, or it will be a member of the Complete_items set.  */
    /* Duplicates can also occur because of the elimination of single    */
    /* productions.                                                      */
    /*********************************************************************/

    state_seen = Allocate_boolean_array(max_la_state + 1);
    item_seen  = Allocate_boolean_array(num_items + 1);
    item_list  = Allocate_short_array(num_items + 1);

/* INITIALIZATION -----------------------------------------------------------*/

    for ALL_STATES(i)
        state_seen[i] = FALSE;

    for ALL_ITEMS(i)
        item_seen[i] = FALSE;

    kernel_size = 0;

/* END OF INITIALIZATION ----------------------------------------------------*/

    i = number_len(state_no) + 8; /* 8 = length("STATE") + 2 spaces + newline*/
    fill_in(buffer, (PRINT_LINE_SIZE - i) ,'-');

    fprintf(syslis, "\n\n\nSTATE %d %s",state_no, buffer);
    output_line_no +=3;

    /*********************************************************************/
    /* Print the set of states that have transitions to STATE_NO.        */
    /*********************************************************************/
    n = 0;
    strcpy(line, "( ");

    for (end_node = ((q = in_stat[state_no]) == NULL);
         ! end_node;
         end_node = (q == in_stat[state_no]))
    {/* copy list of IN_STAT into array */
        q = q -> next;
        if (! state_seen[q -> value])
        {
            state_seen[q -> value] = TRUE;
            if (strlen(line) + number_len(q -> value) > PRINT_LINE_SIZE-2)
            {
                fprintf(syslis,"\n%s",line);
                ENDPAGE_CHECK;
                strcpy(line, "  ");
            }
            if (q -> value != 0)
            {
                sprintf(buffer, "%d ", q -> value);
                strcat(line, buffer);
            }
        }
    }
    strcat(line, ")");
    fprintf(syslis,"\n%s\n", line);
    output_line_no++;
    ENDPAGE_CHECK;

    /*********************************************************************/
    /* Add the set of kernel items to the array ITEM_LIST, and mark all  */
    /* items seen to avoid duplicates.                                   */
    /*********************************************************************/

    for (q = statset[state_no].kernel_items; q != NULL; q = q -> next)
    {
        kernel_size++;
        item_no = q -> value;
        item_list[kernel_size] = item_no;    /* add to array */
        item_seen[item_no] = TRUE;         /* Mark as "seen" */
    }

    /*********************************************************************/
    /* Add the Complete Items to the array ITEM_LIST, and mark used.     */
    /*********************************************************************/
    n = kernel_size;
    for (q = statset[state_no].complete_items; q != NULL; q = q -> next)
    {
        item_no = q -> value;
        if (! item_seen[item_no])
        {
            item_seen[item_no] = TRUE; /* Mark as "seen" */
            item_list[++n] = item_no;
        }
    }

    /*********************************************************************/
    /* Iterate over the shift map.  Shift-Reduce actions are identified  */
    /* by a negative integer that indicates the rule in question , and   */
    /* the associated item can be retrieved by indexing the array        */
    /* ADEQUATE_ITEMS at the location of the rule.  For shift-actions, we*/
    /* simply take the predecessor-items of all the items in the kernel  */
    /* of the following state.                                           */
    /* If the shift-action is a look-ahead shift, we check to see if the */
    /* look-ahead state contains shift actions, and retrieve the next    */
    /* state from one of those shift actions.                            */
    /*********************************************************************/
    sh = shift[statset[state_no].shift_number];
    for (i = 1; i <= sh.size; i++)
    {
        next_state = SHIFT_ACTION(sh, i);
        while (next_state > (int) num_states)
        {
            struct shift_header_type next_sh;

            next_sh = shift[lastats[next_state].shift_number];
            if (next_sh.size > 0)
                next_state = SHIFT_ACTION(next_sh, 1);
            else
                next_state = 0;
        }

        if (next_state == 0)
            q = NULL;
        else if (next_state < 0)
            q = adequate_item[-next_state];
        else
        {
            q = statset[next_state].kernel_items;
            if (q == NULL) /* single production state? */
                q = statset[next_state].complete_items;
        }
        for (; q != NULL; q = q -> next)
        {
            item_no = q -> value - 1;
            if (! item_seen[item_no])
            {
                item_seen[item_no] = TRUE;
                item_list[++n] = item_no;
            }
        }
    }

    /*********************************************************************/
    /* GOTOS and GOTO-REDUCES are analogous to SHIFTS and SHIFT-REDUCES. */
    /*********************************************************************/
    go_to = statset[state_no].go_to;
    for (i = 1; i <= go_to.size; i++)
    {
        if (GOTO_ACTION(go_to, i) > 0)
        {
            q = statset[GOTO_ACTION(go_to, i)].kernel_items;
            if (q == NULL)          /* single production state? */
                q = statset[GOTO_ACTION(go_to, i)].complete_items;
        }
        else
            q = adequate_item[- GOTO_ACTION(go_to, i)];

        for (; q != NULL; q = q -> next)
        {
            item_no = q -> value - 1;
            if (! item_seen[item_no])
            {
                item_seen[item_no] = TRUE;
                item_list[++n] = item_no;
            }
        }
    }

    /*********************************************************************/
    /* Print the Kernel items.  If there are any closure items, skip a   */
    /* line, sort then, then print them.  The kernel items are in sorted */
    /* order.                                                            */
    /*********************************************************************/
    for (item_no = 1; item_no <= kernel_size; item_no++)
         print_item(item_list[item_no]);
    if (kernel_size < n)
    {
        fprintf(syslis, "\n");
        ENDPAGE_CHECK;
        qcksrt(item_list, kernel_size + 1, n);
        for (item_no = kernel_size + 1; item_no <= n; item_no++)
            print_item(item_list[item_no]);
    }

    ffree(item_list);
    ffree(item_seen);
    ffree(state_seen);

    return;
}


/*****************************************************************************/
/*                                 NOSPACE:                                  */
/*****************************************************************************/
/* This procedure is invoked when a call to MALLOC, CALLOC or REALLOC fails. */
/*****************************************************************************/
void nospace(char *file_name, long line_number)
{
    fprintf(stderr,
            "*** Cannot allocate space ... LPG terminated in "
            "file %s at line %d\n", file_name, line_number);
    exit(12);
}


/************************************************************************/
/*                               STRUPR:                                */
/************************************************************************/
/* StrUpr and StrLwr.                                                   */
/* These routines set all characters in a string to upper case and lower*/
/*  case (respectively.)  These are library routines in DOS, but        */
/*  are defined here for the 370 compiler.                              */
/************************************************************************/
char *strupr(char *string)
{
    char *s;

    /*********************************************************************/
    /* While not at end of string, change all lower case to upper case.  */
    /*********************************************************************/
    for (s = string; *s != '\0'; s++)
        *s = (islower((int) *s) ? toupper((int) *s) : *s);

    return string;
}

/************************************************************************/
/*                               STRLWR:                                */
/************************************************************************/
char *strlwr(char *string)
{
    char *s;

    /*********************************************************************/
    /* While not at end of string, change all upper case to lower case.  */
    /*********************************************************************/
    for (s = string; *s != '\0'; s++)
        *s = (isupper((int) *s) ? tolower((int) *s) : *s);

    return string;
}
