/****************************************************************
Copyright 1990, 1991 by AT&T Bell Laboratories and Bellcore.

Permission to use, copy, modify, and distribute this software
and its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the names of AT&T Bell Laboratories or
Bellcore or any of their entities not be used in advertising or
publicity pertaining to distribution of the software without
specific, written prior permission.

AT&T and Bellcore disclaim all warranties with regard to this
software, including all implied warranties of merchantability
and fitness.  In no event shall AT&T or Bellcore be liable for
any special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether
in an action of contract, negligence or other tortious action,
arising out of or in connection with the use or performance of
this software.
****************************************************************/

/* Format.c -- this file takes an intermediate file (generated by pass 1
   of the translator) and some state information about the contents of that
   file, and generates C program text. */

#include "defs.h"
#include "p1defs.h"
#include "format.h"
#include "output.h"
#include "names.h"
#include "iob.h"

int c_output_line_length = DEF_C_LINE_LENGTH;

int last_was_label;	/* Boolean used to generate semicolons
				   when a label terminates a block */
static char this_proc_name[52];	/* Name of the current procedure.  This is
				   probably too simplistic to handle
				   multiple entry points */

static int p1getd(), p1gets(), p1getf(), get_p1_token();
static int p1get_const(), p1getn();
static expptr do_format(), do_p1_name_pointer(), do_p1_const();
static expptr do_p1_expr(), do_p1_ident(), do_p1_charp(), do_p1_extern();
static expptr do_p1_head(), do_p1_list(), do_p1_literal();
static void do_p1_label(), do_p1_asgoto(), do_p1_goto();
static void do_p1_if(), do_p1_else(), do_p1_elif(), do_p1_endif();
static void do_p1_endelse(), do_p1_subr_ret(), do_p1_comp_goto();
static void do_p1_for(), do_p1_end_for(), do_p1_fortran();
static void do_p1_1while(), do_p1_2while(), do_p1_elseifstart();
static void do_p1_comment(), do_p1_set_line();
static expptr do_p1_addr();
static void proto();
void list_arg_types();
chainp length_comp();
void listargs();
extern chainp assigned_fmts;
static long old_lineno;
static char filename[P1_FILENAME_MAX];
extern int gflag;
extern char *parens;

start_formatting ()
{
    FILE *infile;
    static int wrote_one = 0;
    extern int usedefsforcommon;
    extern char *p1_file, *p1_bakfile;

    this_proc_name[0] = '\0';
    last_was_label = 0;
    old_lineno = lineno;
    ei_next = ei_first;
    wh_next = wh_first;

    (void) fclose (pass1_file);
    if ((infile = fopen (p1_file, binread)) == NULL)
	Fatal("start_formatting:  couldn't open the intermediate file\n");

    if (wrote_one)
	nice_printf (c_file, "\n");

    while (!feof (infile)) {
	expptr this_expr;

	this_expr = do_format (infile, c_file);
	if (this_expr) {
	    out_and_free_statement (c_file, this_expr);
	} /* if this_expr */
    } /* while !feof infile */

    (void) fclose (infile);

    if (last_was_label)
	nice_printf (c_file, ";\n");

    prev_tab (c_file);
    if (this_proc_name[0])
	nice_printf (c_file, "} /* %s */\n", this_proc_name);


/* Write the #undefs for common variable reference */

    if (usedefsforcommon) {
	Extsym *ext;
	int did_one = 0;

	for (ext = extsymtab; ext < nextext; ext++)
	    if (ext -> extstg == STGCOMMON && ext -> used_here) {
		ext -> used_here = 0;
		if (!did_one)
		    nice_printf (c_file, "\n");
		wr_abbrevs(c_file, 0, ext->extp);
		did_one = 1;
		ext -> extp = CHNULL;
	    } /* if */

	if (did_one)
	    nice_printf (c_file, "\n");
    } /* if usedefsforcommon */

    other_undefs(c_file);

    wrote_one = 1;

/* For debugging only */

    if (debugflag && (pass1_file = fopen (p1_bakfile, binwrite)))
	if (infile = fopen (p1_file, binread)) {
	    ffilecopy (infile, pass1_file);
	    fclose (infile);
	    fclose (pass1_file);
	} /* if infile */

/* End of "debugging only" */

    scrub(p1_file);	/* optionally unlink */

    if ((pass1_file = fopen (p1_file, binwrite)) == NULL)
	err ("start_formatting:  couldn't reopen the pass1 file");

} /* start_formatting */


 static void
put_semi(outfile)
 FILE *outfile;
{
	nice_printf (outfile, ";\n");
	last_was_label = 0;
	}

#define SEM_CHECK(x) if (last_was_label) put_semi(x)

/* do_format -- takes an input stream (a file in pass1 format) and writes
   the appropriate C code to   outfile   when possible.  When reading an
   expression, the expression tree is returned instead. */

static expptr do_format (infile, outfile)
FILE *infile, *outfile;
{
    int gsave, token_type, was_c_token;
    expptr retval = ENULL;

    token_type = get_p1_token (infile);
    was_c_token = 1;
    switch (token_type) {
	case P1_COMMENT:
	    do_p1_comment (infile, outfile);
	    was_c_token = 0;
	    break;
	case P1_SET_LINE:
	    do_p1_set_line (infile);
	    was_c_token = 0;
	    break;
	case P1_FILENAME:
	    p1gets(infile, filename, P1_FILENAME_MAX);
	    was_c_token = 0;
	    break;
	case P1_NAME_POINTER:
	    retval = do_p1_name_pointer (infile);
	    break;
	case P1_CONST:
	    retval = do_p1_const (infile);
	    break;
	case P1_EXPR:
	    retval = do_p1_expr (infile, outfile);
	    break;
	case P1_IDENT:
	    retval = do_p1_ident(infile);
	    break;
	case P1_CHARP:
		retval = do_p1_charp(infile);
		break;
	case P1_EXTERN:
	    retval = do_p1_extern (infile);
	    break;
	case P1_HEAD:
	    gsave = gflag;
	    gflag = 0;
	    retval = do_p1_head (infile, outfile);
	    gflag = gsave;
	    break;
	case P1_LIST:
	    retval = do_p1_list (infile, outfile);
	    break;
	case P1_LITERAL:
	    retval = do_p1_literal (infile);
	    break;
	case P1_LABEL:
	    do_p1_label (infile, outfile);
	    /* last_was_label = 1; -- now set in do_p1_label */
	    was_c_token = 0;
	    break;
	case P1_ASGOTO:
	    do_p1_asgoto (infile, outfile);
	    break;
	case P1_GOTO:
	    do_p1_goto (infile, outfile);
	    break;
	case P1_IF:
	    do_p1_if (infile, outfile);
	    break;
	case P1_ELSE:
	    SEM_CHECK(outfile);
	    do_p1_else (outfile);
	    break;
	case P1_ELIF:
	    SEM_CHECK(outfile);
	    do_p1_elif (infile, outfile);
	    break;
	case P1_ENDIF:
	    SEM_CHECK(outfile);
	    do_p1_endif (outfile);
	    break;
	case P1_ENDELSE:
	    SEM_CHECK(outfile);
	    do_p1_endelse (outfile);
	    break;
	case P1_ADDR:
	    retval = do_p1_addr (infile, outfile);
	    break;
	case P1_SUBR_RET:
	    do_p1_subr_ret (infile, outfile);
	    break;
	case P1_COMP_GOTO:
	    do_p1_comp_goto (infile, outfile);
	    break;
	case P1_FOR:
	    do_p1_for (infile, outfile);
	    break;
	case P1_ENDFOR:
	    SEM_CHECK(outfile);
	    do_p1_end_for (outfile);
	    break;
	case P1_WHILE1START:
		do_p1_1while(outfile);
		break;
	case P1_WHILE2START:
		do_p1_2while(infile, outfile);
		break;
	case P1_PROCODE:
		procode(outfile);
		break;
	case P1_ELSEIFSTART:
		SEM_CHECK(outfile);
		do_p1_elseifstart(outfile);
		break;
	case P1_FORTRAN:
		do_p1_fortran(infile, outfile);
		/* no break; */
	case P1_EOF:
	    was_c_token = 0;
	    break;
	case P1_UNKNOWN:
	    Fatal("do_format:  Unknown token type in intermediate file");
	    break;
	default:
	    Fatal("do_format:  Bad token type in intermediate file");
	    break;
   } /* switch */

    if (was_c_token)
	last_was_label = 0;
    return retval;
} /* do_format */


 static void
do_p1_comment (infile, outfile)
FILE *infile, *outfile;
{
    extern int c_output_line_length, in_comment;

    char storage[COMMENT_BUFFER_SIZE + 1];
    int length;

    if (!p1gets(infile, storage, COMMENT_BUFFER_SIZE + 1))
	return;

    length = strlen (storage);

    in_comment = 1;
    if (length > c_output_line_length - 6)
	margin_printf (outfile, "/*%s*/\n", storage);
    else
	margin_printf (outfile, length ? "/* %s */\n" : "\n", storage);
    in_comment = 0;
} /* do_p1_comment */

 static void
do_p1_set_line (infile)
FILE *infile;
{
    int status;
    long new_line_number = -1;

    status = p1getd (infile, &new_line_number);

    if (status == EOF)
	err ("do_p1_set_line:  Missing line number at end of file\n");
    else if (status == 0 || new_line_number == -1)
	errl("do_p1_set_line:  Illegal line number in intermediate file: %ld\n",
		new_line_number);
    else {
	lineno = new_line_number;
	if (gflag)
		fprintf(c_file, "/*# %ld \"%s\"*/\n", lineno, filename);
	}
} /* do_p1_set_line */


static expptr do_p1_name_pointer (infile)
FILE *infile;
{
    Namep namep = (Namep) NULL;
    int status;

    status = p1getd (infile, (long *) &namep);

    if (status == EOF)
	err ("do_p1_name_pointer:  Missing pointer at end of file\n");
    else if (status == 0 || namep == (Namep) NULL)
	erri ("do_p1_name_pointer:  Illegal name pointer in p1 file: '%x'\n",
		(int) namep);

    return (expptr) namep;
} /* do_p1_name_pointer */



static expptr do_p1_const (infile)
FILE *infile;
{
    struct Constblock *c = (struct Constblock *) NULL;
    long type = -1;
    int status;

    status = p1getd (infile, &type);

    if (status == EOF)
	err ("do_p1_const:  Missing constant type at end of file\n");
    else if (status == 0)
	errl("do_p1_const:  Illegal constant type in p1 file: %ld\n", type);
    else {
	status = p1get_const (infile, (int)type, &c);

	if (status == EOF) {
	    err ("do_p1_const:  Missing constant value at end of file\n");
	    c = (struct Constblock *) NULL;
	} else if (status == 0) {
	    err ("do_p1_const:  Illegal constant value in p1 file\n");
	    c = (struct Constblock *) NULL;
	} /* else */
    } /* else */
    return (expptr) c;
} /* do_p1_const */


static expptr do_p1_literal (infile)
FILE *infile;
{
    int status;
    long memno;
    Addrp addrp;

    status = p1getd (infile, &memno);

    if (status == EOF)
	err ("do_p1_literal:  Missing memno at end of file");
    else if (status == 0)
	err ("do_p1_literal:  Missing memno in p1 file");
    else {
	struct Literal *litp, *lastlit;

	addrp = ALLOC (Addrblock);
	addrp -> tag = TADDR;
	addrp -> vtype = TYUNKNOWN;
	addrp -> Field = NULL;

	lastlit = litpool + nliterals;
	for (litp = litpool; litp < lastlit; litp++)
	    if (litp -> litnum == memno) {
		addrp -> vtype = litp -> littype;
		*((union Constant *) &(addrp -> user)) =
			*((union Constant *) &(litp -> litval));
		break;
	    } /* if litp -> litnum == memno */

	addrp -> memno = memno;
	addrp -> vstg = STGMEMNO;
	addrp -> uname_tag = UNAM_CONST;
    } /* else */

    return (expptr) addrp;
} /* do_p1_literal */


static void do_p1_label (infile, outfile)
FILE *infile, *outfile;
{
    int status;
    ftnint stateno;
    char *user_label ();
    struct Labelblock *L;
    char *fmt;

    status = p1getd (infile, &stateno);

    if (status == EOF)
	err ("do_p1_label:  Missing label at end of file");
    else if (status == 0)
	err ("do_p1_label:  Missing label in p1 file ");
    else if (stateno < 0) {	/* entry */
	margin_printf(outfile, "\n%s:\n", user_label(stateno));
	last_was_label = 1;
	}
    else {
	L = labeltab + stateno;
	if (L->labused) {
		fmt = "%s:\n";
		last_was_label = 1;
		}
	else
		fmt = "/* %s: */\n";
	margin_printf(outfile, fmt, user_label(L->stateno));
    } /* else */
} /* do_p1_label */



static void do_p1_asgoto (infile, outfile)
FILE *infile, *outfile;
{
    expptr expr;

    expr = do_format (infile, outfile);
    out_asgoto (outfile, expr);

} /* do_p1_asgoto */


static void do_p1_goto (infile, outfile)
FILE *infile, *outfile;
{
    int status;
    long stateno;
    char *user_label ();

    status = p1getd (infile, &stateno);

    if (status == EOF)
	err ("do_p1_goto:  Missing goto label at end of file");
    else if (status == 0)
	err ("do_p1_goto:  Missing goto label in p1 file");
    else {
	nice_printf (outfile, "goto %s;\n", user_label (stateno));
    } /* else */
} /* do_p1_goto */


static void do_p1_if (infile, outfile)
FILE *infile, *outfile;
{
    expptr cond;

    do {
        cond = do_format (infile, outfile);
    } while (cond == ENULL);

    out_if (outfile, cond);
} /* do_p1_if */


static void do_p1_else (outfile)
FILE *outfile;
{
    out_else (outfile);
} /* do_p1_else */


static void do_p1_elif (infile, outfile)
FILE *infile, *outfile;
{
    expptr cond;

    do {
        cond = do_format (infile, outfile);
    } while (cond == ENULL);

    elif_out (outfile, cond);
} /* do_p1_elif */

static void do_p1_endif (outfile)
FILE *outfile;
{
    endif_out (outfile);
} /* do_p1_endif */


static void do_p1_endelse (outfile)
FILE *outfile;
{
    end_else_out (outfile);
} /* do_p1_endelse */


static expptr do_p1_addr (infile, outfile)
FILE *infile, *outfile;
{
    Addrp addrp = (Addrp) NULL;
    int status;

    status = p1getn (infile, sizeof (struct Addrblock), (char **) &addrp);

    if (status == EOF)
	err ("do_p1_addr:  Missing Addrp at end of file");
    else if (status == 0)
	err ("do_p1_addr:  Missing Addrp in p1 file");
    else if (addrp == (Addrp) NULL)
	err ("do_p1_addr:  Null addrp in p1 file");
    else if (addrp -> tag != TADDR)
	erri ("do_p1_addr: bad tag in p1 file '%d'", addrp -> tag);
    else {
	addrp -> vleng = do_format (infile, outfile);
	addrp -> memoffset = do_format (infile, outfile);
    }

    return (expptr) addrp;
} /* do_p1_addr */



static void do_p1_subr_ret (infile, outfile)
FILE *infile, *outfile;
{
    expptr retval;

    nice_printf (outfile, "return ");
    retval = do_format (infile, outfile);
    if (!multitype)
	if (retval)
		expr_out (outfile, retval);

    nice_printf (outfile, ";\n");
} /* do_p1_subr_ret */



static void do_p1_comp_goto (infile, outfile)
FILE *infile, *outfile;
{
    expptr index;
    expptr labels;

    index = do_format (infile, outfile);

    if (index == ENULL) {
	err ("do_p1_comp_goto:  no expression for computed goto");
	return;
    } /* if index == ENULL */

    labels = do_format (infile, outfile);

    if (labels && labels -> tag != TLIST)
	erri ("do_p1_comp_goto:  expected list, got tag '%d'", labels -> tag);
    else
	compgoto_out (outfile, index, labels);
} /* do_p1_comp_goto */


static void do_p1_for (infile, outfile)
FILE *infile, *outfile;
{
    expptr init, test, inc;

    init = do_format (infile, outfile);
    test = do_format (infile, outfile);
    inc = do_format (infile, outfile);

    out_for (outfile, init, test, inc);
} /* do_p1_for */

static void do_p1_end_for (outfile)
FILE *outfile;
{
    out_end_for (outfile);
} /* do_p1_end_for */


 static void
do_p1_fortran(infile, outfile)
 FILE *infile, *outfile;
{
	char buf[P1_STMTBUFSIZE];
	if (!p1gets(infile, buf, P1_STMTBUFSIZE))
		return;
	/* bypass nice_printf nonsense */
	fprintf(outfile, "/*< %s >*/\n", buf+1);	/* + 1 to skip by '$' */
	}


static expptr do_p1_expr (infile, outfile)
FILE *infile, *outfile;
{
    int status;
    long opcode, type;
    struct Exprblock *result = (struct Exprblock *) NULL;

    status = p1getd (infile, &opcode);

    if (status == EOF)
	err ("do_p1_expr:  Missing expr opcode at end of file");
    else if (status == 0)
	err ("do_p1_expr:  Missing expr opcode in p1 file");
    else {

	status = p1getd (infile, &type);

	if (status == EOF)
	    err ("do_p1_expr:  Missing expr type at end of file");
	else if (status == 0)
	    err ("do_p1_expr:  Missing expr type in p1 file");
	else if (opcode == 0)
	    return ENULL;
	else {
	    result = ALLOC (Exprblock);

	    result -> tag = TEXPR;
	    result -> vtype = type;
	    result -> opcode = opcode;
	    result -> vleng = do_format (infile, outfile);

	    if (is_unary_op (opcode))
		result -> leftp = do_format (infile, outfile);
	    else if (is_binary_op (opcode)) {
		result -> leftp = do_format (infile, outfile);
		result -> rightp = do_format (infile, outfile);
	    } else
		errl("do_p1_expr:  Illegal opcode %ld", opcode);
	} /* else */
    } /* else */

    return (expptr) result;
} /* do_p1_expr */


static expptr do_p1_ident(infile)
FILE *infile;
{
	Addrp addrp;
	int status;
	long vtype, vstg;

	addrp = ALLOC (Addrblock);
	addrp -> tag = TADDR;

	status = p1getd (infile, &vtype);
	if (status == EOF)
	    err ("do_p1_ident:  Missing identifier type at end of file\n");
	else if (status == 0 || vtype < 0 || vtype >= NTYPES)
	    errl("do_p1_ident:  Bad type in intermediate file: %ld\n", vtype);
	else
	    addrp -> vtype = vtype;

	status = p1getd (infile, &vstg);
	if (status == EOF)
	    err ("do_p1_ident:  Missing identifier storage at end of file\n");
	else if (status == 0 || vstg < 0 || vstg > STGNULL)
	    errl("do_p1_ident:  Bad storage in intermediate file: %ld\n", vtype);
	else
	    addrp -> vstg = vstg;

	status = p1gets(infile, addrp->user.ident, IDENT_LEN);

	if (status == EOF)
	    err ("do_p1_ident:  Missing ident string at end of file");
	else if (status == 0)
	    err ("do_p1_ident:  Missing ident string in intermediate file");
	addrp->uname_tag = UNAM_IDENT;
	return (expptr) addrp;
} /* do_p1_ident */

static expptr do_p1_charp(infile)
FILE *infile;
{
	Addrp addrp;
	int status;
	long vtype, vstg;
	char buf[64];

	addrp = ALLOC (Addrblock);
	addrp -> tag = TADDR;

	status = p1getd (infile, &vtype);
	if (status == EOF)
	    err ("do_p1_ident:  Missing identifier type at end of file\n");
	else if (status == 0 || vtype < 0 || vtype >= NTYPES)
	    errl("do_p1_ident:  Bad type in intermediate file: %ld\n", vtype);
	else
	    addrp -> vtype = vtype;

	status = p1getd (infile, &vstg);
	if (status == EOF)
	    err ("do_p1_ident:  Missing identifier storage at end of file\n");
	else if (status == 0 || vstg < 0 || vstg > STGNULL)
	    errl("do_p1_ident:  Bad storage in intermediate file: %ld\n", vtype);
	else
	    addrp -> vstg = vstg;

	status = p1gets(infile, buf, (int)sizeof(buf));

	if (status == EOF)
	    err ("do_p1_ident:  Missing charp ident string at end of file");
	else if (status == 0)
	    err ("do_p1_ident:  Missing charp ident string in intermediate file");
	addrp->uname_tag = UNAM_CHARP;
	addrp->user.Charp = strcpy(mem(strlen(buf)+1,0), buf);
	return (expptr) addrp;
}


static expptr do_p1_extern (infile)
FILE *infile;
{
    Addrp addrp;

    addrp = ALLOC (Addrblock);
    if (addrp) {
	int status;

	addrp->tag = TADDR;
	addrp->vstg = STGEXT;
	addrp->uname_tag = UNAM_EXTERN;
	status = p1getd (infile, &(addrp -> memno));
	if (status == EOF)
	    err ("do_p1_extern:  Missing memno at end of file");
	else if (status == 0)
	    err ("do_p1_extern:  Missing memno in intermediate file");
	if (addrp->vtype = extsymtab[addrp->memno].extype)
		addrp->vclass = CLPROC;
    } /* if addrp */

    return (expptr) addrp;
} /* do_p1_extern */



static expptr do_p1_head (infile, outfile)
FILE *infile, *outfile;
{
    int status;
    int add_n_;
    long class;
    char storage[256];

    status = p1getd (infile, &class);
    if (status == EOF)
	err ("do_p1_head:  missing header class at end of file");
    else if (status == 0)
	err ("do_p1_head:  missing header class in p1 file");
    else {
	status = p1gets (infile, storage, (int)sizeof(storage));
	if (status == EOF || status == 0)
	    storage[0] = '\0';
    } /* else */

    if (class == CLPROC || class == CLMAIN) {
	chainp lengths;

	add_n_ = nentry > 1;
	lengths = length_comp(entries, add_n_);

	if (!add_n_ && protofile && class != CLMAIN)
		protowrite(protofile, proctype, storage, entries, lengths);

	if (class == CLMAIN)
	    nice_printf (outfile, "/* Main program */ ");
	else
	    nice_printf(outfile, "%s ", multitype ? "VOID"
			: c_type_decl(proctype, 1));

	nice_printf(outfile, add_n_ ? "%s0_" : "%s", storage);
	if (!Ansi) {
		listargs(outfile, entries, add_n_, lengths);
		nice_printf (outfile, "\n");
		}
	list_arg_types (outfile, entries, lengths, add_n_, "\n");
	nice_printf (outfile, "{\n");
	frchain(&lengths);
	next_tab (outfile);
	strcpy(this_proc_name, storage);
	list_decls (outfile);

    } else if (class == CLBLOCK)
        next_tab (outfile);
    else
	errl("do_p1_head: got class %ld", class);

    return NULL;
} /* do_p1_head */


static expptr do_p1_list (infile, outfile)
FILE *infile, *outfile;
{
    long tag, type, count;
    int status;
    expptr result;

    status = p1getd (infile, &tag);
    if (status == EOF)
	err ("do_p1_list:  missing list tag at end of file");
    else if (status == 0)
	err ("do_p1_list:  missing list tag in p1 file");
    else {
	status = p1getd (infile, &type);
	if (status == EOF)
	    err ("do_p1_list:  missing list type at end of file");
	else if (status == 0)
	    err ("do_p1_list:  missing list type in p1 file");
	else {
	    status = p1getd (infile, &count);
	    if (status == EOF)
		err ("do_p1_list:  missing count at end of file");
	    else if (status == 0)
		err ("do_p1_list:  missing count in p1 file");
	} /* else */
    } /* else */

    result = (expptr) ALLOC (Listblock);
    if (result) {
	chainp pointer;

	result -> tag = tag;
	result -> listblock.vtype = type;

/* Assume there will be enough data */

	if (count--) {
	    pointer = result->listblock.listp =
		mkchain((char *)do_format(infile, outfile), CHNULL);
	    while (count--) {
		pointer -> nextp =
			mkchain((char *)do_format(infile, outfile), CHNULL);
		pointer = pointer -> nextp;
	    } /* while (count--) */
	} /* if (count) */
    } /* if (result) */

    return result;
} /* do_p1_list */


chainp length_comp(e, add_n)	/* get lengths of characters args */
 struct Entrypoint *e;
 int add_n;
{
	chainp lengths;
	chainp args, args1;
	Namep arg, np;
	int nchargs;
	Argtypes *at;
	Atype *a;
	extern int init_ac[TYSUBR+1];

	args = args1 = add_n ? allargs : e->arglist;
	nchargs = 0;
	for (lengths = NULL; args; args = args -> nextp)
		if (arg = (Namep)args->datap) {
			if (arg->vclass == CLUNKNOWN)
				arg->vclass = CLVAR;
			if (arg->vtype == TYCHAR && arg->vclass != CLPROC) {
				lengths = mkchain((char *)arg, lengths);
				nchargs++;
				}
			}
	if (!add_n && (np = e->enamep)) {
		/* one last check -- by now we know all we ever will
		 * about external args...
		 */
		save_argtypes(e->arglist, &e->entryname->arginfo,
			&np->arginfo, 0, np->fvarname, STGEXT, nchargs,
			np->vtype, 1);
		at = e->entryname->arginfo;
		a = at->atypes + init_ac[np->vtype];
		for(; args1; a++, args1 = args1->nextp) {
			frchain(&a->cp);
			if (arg = (Namep)args1->datap)
			    switch(arg->vclass) {
				case CLPROC:
					if (arg->vimpltype
					&& a->type >= 300)
						a->type = TYUNKNOWN + 200;
					break;
				case CLUNKNOWN:
					a->type %= 100;
				}
			}
		}
	return revchain(lengths);
	}

void listargs(outfile, entryp, add_n_, lengths)
 FILE *outfile;
 struct Entrypoint *entryp;
 int add_n_;
 chainp lengths;
{
	chainp args;
	char *s;
	Namep arg;
	int did_one = 0;

	nice_printf (outfile, "(");

	if (add_n_) {
		nice_printf(outfile, "n__");
		did_one = 1;
		args = allargs;
		}
	else
		args = entryp->arglist;

	if (multitype)
		{
		nice_printf(outfile, ", ret_val");
		did_one = 1;
		args = allargs;
		}
	else if (ONEOF(proctype, MSKCOMPLEX|MSKCHAR))
		{
		s = xretslot[proctype]->user.ident;
		nice_printf(outfile, did_one ? ", %s" : "%s",
			*s == '(' /*)*/ ? "r_v" : s);
		did_one = 1;
		if (proctype == TYCHAR)
			nice_printf (outfile, ", ret_val_len");
		}
	for (; args; args = args -> nextp)
		if (arg = (Namep)args->datap) {
			nice_printf (outfile, "%s", did_one ? ", " : "");
			out_name (outfile, arg);
			did_one = 1;
			}

	for (args = lengths; args; args = args -> nextp)
		nice_printf(outfile, ", %s",
			new_arg_length((Namep)args->datap));
	nice_printf (outfile, ")");
} /* listargs */


void list_arg_types(outfile, entryp, lengths, add_n_, finalnl)
FILE *outfile;
struct Entrypoint *entryp;
chainp lengths;
int add_n_;
char *finalnl;
{
    chainp args;
    int last_type = -1, last_class = -1;
    int did_one = 0, done_one, is_ext;
    char *s, *sep = "", *sep1;

    if (outfile == (FILE *) NULL) {
	err ("list_arg_types:  null output file");
	return;
    } else if (entryp == (struct Entrypoint *) NULL) {
	err ("list_arg_types:  null procedure entry pointer");
	return;
    } /* else */

    if (Ansi) {
	done_one = 0;
	sep1 = ", ";
	nice_printf(outfile, "(" /*)*/);
	}
    else {
	done_one = 1;
	sep1 = ";\n";
	}
    args = entryp->arglist;
    if (add_n_) {
	nice_printf(outfile, "int n__");
	did_one = done_one;
	sep = sep1;
	args = allargs;
	}
    if (multitype) {
	nice_printf(outfile, "%sMultitype *ret_val", sep);
	did_one = done_one;
	sep = sep1;
	}
    else if (ONEOF (proctype, MSKCOMPLEX|MSKCHAR)) {
	s = xretslot[proctype]->user.ident;
	nice_printf(outfile, "%s%s *%s", sep, c_type_decl(proctype, 0),
			*s == '(' /*)*/ ? "r_v" : s);
	did_one = done_one;
	sep = sep1;
	if (proctype == TYCHAR)
	    nice_printf (outfile, "%sftnlen ret_val_len", sep);
    } /* if ONEOF proctype */
    for (; args; args = args -> nextp) {
	Namep arg = (Namep) args->datap;

/* Scalars are passed by reference, and arrays will have their lower bound
   adjusted, so nearly everything is printed with a star in front.  The
   exception is character lengths, which are passed by value. */

	if (arg) {
	    int type = arg -> vtype, class = arg -> vclass;

	    if (class == CLPROC)
		if (arg->vimpltype)
			type = Castargs ? TYUNKNOWN : TYSUBR;
		else if (type == TYREAL && forcedouble && !Castargs)
			type = TYDREAL;

	    if (type == last_type && class == last_class && did_one)
		nice_printf (outfile, ", ");
	    else
		if ((is_ext = class == CLPROC) && Castargs)
			nice_printf(outfile, "%s%s ", sep,
				usedcasts[type] = casttypes[type]);
		else
			nice_printf(outfile, "%s%s ", sep,
				c_type_decl(type, is_ext));
	    if (class == CLPROC)
		if (Castargs)
			out_name(outfile, arg);
		else {
			nice_printf(outfile, "(*");
			out_name(outfile, arg);
			nice_printf(outfile, ") %s", parens);
			}
	    else {
		nice_printf (outfile, "*");
		out_name (outfile, arg);
		}

	    last_type = type;
	    last_class = class;
	    did_one = done_one;
	    sep = sep1;
	} /* if (arg) */
    } /* for args = entryp -> arglist */

    for (args = lengths; args; args = args -> nextp)
	nice_printf(outfile, "%sftnlen %s", sep,
			new_arg_length((Namep)args->datap));
    if (did_one)
	nice_printf (outfile, ";\n");
    else if (Ansi)
	nice_printf(outfile,
		/*((*/ sep != sep1 && Ansi == 1 ? "void)%s" : ")%s",
		finalnl);
} /* list_arg_types */

 static void
write_formats(outfile)
 FILE *outfile;
{
	register struct Labelblock *lp;
	int first = 1;
	char *fs;

	for(lp = labeltab ; lp < highlabtab ; ++lp)
		if (lp->fmtlabused) {
			if (first) {
				first = 0;
				nice_printf(outfile, "/* Format strings */\n");
				}
			nice_printf(outfile, "static char fmt_%ld[] = \"",
				lp->stateno);
			if (!(fs = lp->fmtstring))
				fs = "";
			nice_printf(outfile, "%s\";\n", fs);
			}
	if (!first)
		nice_printf(outfile, "\n");
	}

 static void
write_ioblocks(outfile)
 FILE *outfile;
{
	register iob_data *L;
	register char *f, **s, *sep;

	nice_printf(outfile, "/* Fortran I/O blocks */\n");
	L = iob_list = (iob_data *)revchain((chainp)iob_list);
	do {
		nice_printf(outfile, "static %s %s = { ",
			L->type, L->name);
		sep = 0;
		for(s = L->fields; f = *s; s++) {
			if (sep)
				nice_printf(outfile, sep);
			sep = ", ";
			if (*f == '"') {	/* kludge */
				nice_printf(outfile, "\"");
				nice_printf(outfile, "%s\"", f+1);
				}
			else
				nice_printf(outfile, "%s", f);
			}
		nice_printf(outfile, " };\n");
		}
		while(L = L->next);
	nice_printf(outfile, "\n\n");
	}

 static void
write_assigned_fmts(outfile)
 FILE *outfile;
{
	register chainp cp;
	Namep np;
	int did_one = 0;

	cp = assigned_fmts = revchain(assigned_fmts);
	nice_printf(outfile, "/* Assigned format variables */\nchar ");
	do {
		np = (Namep)cp->datap;
		if (did_one)
			nice_printf(outfile, ", ");
		did_one = 1;
		nice_printf(outfile, "*%s_fmt", np->fvarname);
		}
		while(cp = cp->nextp);
	nice_printf(outfile, ";\n\n");
	}

 static char *
to_upper(s)
 register char *s;
{
	static char buf[64];
	register char *t = buf;
	register int c;
	while(*t++ = (c = *s++) >= 'a' && c <= 'z' ? c + 'A' - 'a' : c);
	return buf;
	}


/* This routine creates static structures representing a namelist.
   Declarations of the namelist and related structures are:

	struct Vardesc {
		char *name;
		char *addr;
		ftnlen *dims;	/* laid out as struct dimensions below *//*
		int  type;
		};
	typedef struct Vardesc Vardesc;

	struct Namelist {
		char *name;
		Vardesc **vars;
		int nvars;
		};

	struct dimensions
		{
		ftnlen numberofdimensions;
		ftnlen numberofelements
		ftnlen baseoffset;
		ftnlen span[numberofdimensions-1];
		};

   If dims is not null, then the corner element of the array is at
   addr.  However,  the element with subscripts (i1,...,in) is at
   addr + sizeoftype * (i1+span[0]*(i2+span[1]*...) - dimp->baseoffset)
*/

 static void
write_namelists(nmch, outfile)
 chainp nmch;
 FILE *outfile;
{
	Namep var;
	struct Hashentry *entry;
	struct Dimblock *dimp;
	int i, nd, type;
	char *comma, *name;
	register chainp q;
	register Namep v;

	nice_printf(outfile, "/* Namelist stuff */\n\n");
	for (entry = hashtab; entry < lasthash; ++entry) {
		if (!(v = entry->varp) || !v->vnamelist)
			continue;
		type = v->vtype;
		name = v->cvarname;
		if (dimp = v->vdim) {
			nd = dimp->ndim;
			nice_printf(outfile,
				"static ftnlen %s_dims[] = { %d, %ld, %ld",
				name, nd,
				dimp->nelt->constblock.Const.ci,
				dimp->baseoffset->constblock.Const.ci);
			for(i = 0, --nd; i < nd; i++)
				nice_printf(outfile, ", %ld",
				  dimp->dims[i].dimsize->constblock.Const.ci);
			nice_printf(outfile, " };\n");
			}
		nice_printf(outfile, "static Vardesc %s_dv = { \"%s\", %s",
			name, to_upper(name),
			type == TYCHAR ? "" : dimp ? "(char *)" : "(char *)&");
		out_name(outfile, v);
		nice_printf(outfile, dimp ? ", %s_dims" : ", (ftnlen *)0", name);
		nice_printf(outfile, ", %ld };\n",
			type != TYCHAR  ? (long)type
					: -v->vleng->constblock.Const.ci);
		}

	do {
		var = (Namep)nmch->datap;
		name = var->cvarname;
		nice_printf(outfile, "\nstatic Vardesc *%s_vl[] = ", name);
		comma = "{";
		i = 0;
		for(q = var->varxptr.namelist ; q ; q = q->nextp) {
			v = (Namep)q->datap;
			if (!v->vnamelist)
				continue;
			i++;
			nice_printf(outfile, "%s &%s_dv", comma, v->cvarname);
			comma = ",";
			}
		nice_printf(outfile, " };\n");
		nice_printf(outfile,
			"static Namelist %s = { \"%s\", %s_vl, %d };\n",
			name, to_upper(name), name, i);
		}
		while(nmch = nmch->nextp);
	nice_printf(outfile, "\n");
	}

/* fixextype tries to infer from usage in previous procedures
   the type of an external procedure declared
   external and passed as an argument but never typed or invoked.
 */

 static int
fixexttype(var)
 Namep var;
{
	Extsym *e;
	int type, type1;
	extern void changedtype();

	type = var->vtype;
	e = &extsymtab[var->vardesc.varno];
	if ((type1 = e->extype) && type == TYUNKNOWN)
		return var->vtype = type1;
	if (var->visused) {
		if (e->exused && type != type1)
			changedtype(var);
		e->exused = 1;
		e->extype = type;
		}
	return type;
	}

list_decls (outfile)
FILE *outfile;
{
    extern chainp used_builtins;
    extern struct Hashentry *hashtab;
    extern ftnint wr_char_len();
    struct Hashentry *entry;
    int write_header = 1;
    int last_class = -1, last_stg = -1;
    Namep var;
    int Alias, Define, did_one, last_type, type;
    extern int def_equivs, useauto;
    extern chainp new_vars;	/* Compiler-generated locals */
    chainp namelists = 0;
    char *ctype;
    long lineno_save = lineno;
    int useauto1 = useauto && !saveall;
    long x;
    extern int hsize;

    lineno = old_lineno;

/* First write out the statically initialized data */

    if (initfile)
	list_init_data(&initfile, initfname, outfile);

/* Next come formats */
    write_formats(outfile);

/* Now write out the system-generated identifiers */

    if (new_vars || nequiv) {
	chainp args, next_var, this_var;
	chainp nv[TYVOID], nv1[TYVOID];
	int i, j;
	Addrp Var;
	Namep arg;

	/* zap unused dimension variables */

	for(args = allargs; args; args = args->nextp) {
		arg = (Namep)args->datap;
		if (this_var = arg->vlastdim) {
			frexpr((tagptr)this_var->datap);
			this_var->datap = 0;
			}
		}

	/* sort new_vars by type, skipping entries just zapped */

	for(i = TYADDR; i < TYVOID; i++)
		nv[i] = 0;
	for(this_var = new_vars; this_var; this_var = next_var) {
		next_var = this_var->nextp;
		if (Var = (Addrp)this_var->datap) {
			if (!(this_var->nextp = nv[j = Var->vtype]))
				nv1[j] = this_var;
			nv[j] = this_var;
			}
		else {
			this_var->nextp = 0;
			frchain(&this_var);
			}
		}
	new_vars = 0;
	for(i = TYVOID; --i >= TYADDR;)
		if (this_var = nv[i]) {
			nv1[i]->nextp = new_vars;
			new_vars = this_var;
			}

	/* write the declarations */

	did_one = 0;
	last_type = -1;

	for (this_var = new_vars; this_var; this_var = this_var -> nextp) {
	    Var = (Addrp) this_var->datap;

	    if (Var == (Addrp) NULL)
		err ("list_decls:  null variable");
	    else if (Var -> tag != TADDR)
		erri ("list_decls:  bad tag on new variable '%d'",
			Var -> tag);

	    type = nv_type (Var);
	    if (Var->vstg == STGINIT
	    ||  Var->uname_tag == UNAM_IDENT
			&& *Var->user.ident == ' '
			&& multitype)
		continue;
	    if (!did_one)
		nice_printf (outfile, "/* System generated locals */\n");

	    if (last_type == type && did_one)
		nice_printf (outfile, ", ");
	    else {
		if (did_one)
		    nice_printf (outfile, ";\n");
		nice_printf (outfile, "%s ",
			c_type_decl (type, Var -> vclass == CLPROC));
	    } /* else */

/* Character type is really a string type.  Put out a '*' for parameters
   with unknown length and functions returning character */

	    if (Var -> vtype == TYCHAR && (!ISICON ((Var -> vleng))
		    || Var -> vclass == CLPROC))
		nice_printf (outfile, "*");

	    write_nv_ident(outfile, (Addrp)this_var->datap);
	    if (Var -> vtype == TYCHAR && Var->vclass != CLPROC &&
		    ISICON((Var -> vleng))
			&& (i = Var->vleng->constblock.Const.ci) > 0)
		nice_printf (outfile, "[%d]", i);

	    did_one = 1;
	    last_type = nv_type (Var);
	} /* for this_var */

/* Handle the uninitialized equivalences */

	do_uninit_equivs (outfile, &did_one);

	if (did_one)
	    nice_printf (outfile, ";\n\n");
    } /* if new_vars */

/* Write out builtin declarations */

    if (used_builtins) {
	chainp cp;
	Extsym *es;

	last_type = -1;
	did_one = 0;

	nice_printf (outfile, "/* Builtin functions */");

	for (cp = used_builtins; cp; cp = cp -> nextp) {
	    Addrp e = (Addrp)cp->datap;

	    switch(type = e->vtype) {
		case TYDREAL:
		case TYREAL:
			/* if (forcedouble || e->dbl_builtin) */
			/* libF77 currently assumes everything double */
			type = TYDREAL;
			ctype = "double";
			break;
		case TYCOMPLEX:
		case TYDCOMPLEX:
			type = TYVOID;
			/* no break */
		default:
			ctype = c_type_decl(type, 0);
		}

	    if (did_one && last_type == type)
		nice_printf(outfile, ", ");
	    else
		nice_printf(outfile, "%s\n%s ", did_one ? ";" : "", ctype);

	    extern_out(outfile, es = &extsymtab[e -> memno]);
	    proto(outfile, es->arginfo, es->fextname);
	    last_type = type;
	    did_one = 1;
	} /* for cp = used_builtins */

	nice_printf (outfile, ";\n\n");
    } /* if used_builtins */

    last_type = -1;
    for (entry = hashtab; entry < lasthash; ++entry) {
	var = entry -> varp;

	if (var) {
	    int procclass = var -> vprocclass;
	    char *comment = NULL;
	    int stg = var -> vstg;
	    int class = var -> vclass;
	    type = var -> vtype;

	    if (ONEOF(stg, M(STGARG)|M(STGLENG)|M(STGINIT)))
		continue;

	    if (useauto1 && stg == STGBSS && !var->vsave)
		stg = STGAUTO;

	    switch (class) {
	        case CLVAR:
		    break;
		case CLPROC:
		    switch(procclass) {
			case PTHISPROC:
				extsymtab[var->vardesc.varno].extype = type;
				continue;
			case PSTFUNCT:
			case PINTRINSIC:
				continue;
			case PUNKNOWN:
				err ("list_decls:  unknown procedure class");
				continue;
			case PEXTERNAL:
				if (stg == STGUNKNOWN) {
					warn1(
					"%.64s declared EXTERNAL but never used.",
						var->fvarname);
					/* to retain names declared EXTERNAL */
					/* but not referenced, change
					/* "continue" to "stg = STGEXT" */
					continue;
					}
				else
					type = fixexttype(var);
			}
		    break;
		case CLUNKNOWN:
			/* declared but never used */
			continue;
		case CLPARAM:
			continue;
		case CLNAMELIST:
			if (var->visused)
				namelists = mkchain((char *)var, namelists);
			continue;
		default:
		    erri("list_decls:  can't handle class '%d' yet",
			    class);
		    Fatal(var->fvarname);
		    continue;
	    } /* switch */

	    /* Might be equivalenced to a common.  If not, don't process */
	    if (stg == STGCOMMON && !var->vcommequiv)
		continue;

/* Only write the header if system-generated locals, builtins, or
   uninitialized equivs were already output */

	    if (write_header == 1 && (new_vars || nequiv || used_builtins)
		    && oneof_stg ( var, stg,
		    M(STGBSS)|M(STGEXT)|M(STGAUTO)|M(STGCOMMON)|M(STGEQUIV))) {
		nice_printf (outfile, "/* Local variables */\n");
		write_header = 2;
		}


	    Alias = oneof_stg(var, stg, M(STGEQUIV)|M(STGCOMMON));
	    if (Define = (Alias && def_equivs)) {
		if (!write_header)
			nice_printf(outfile, ";\n");
		def_start(outfile, var->cvarname, CNULL, "(");
		goto Alias1;
		}
	    else if (type == last_type && class == last_class &&
		    stg == last_stg && !write_header)
		nice_printf (outfile, ", ");
	    else {
		if (!write_header && ONEOF(stg, M(STGBSS)|
		    M(STGEXT)|M(STGAUTO)|M(STGEQUIV)|M(STGCOMMON)))
		    nice_printf (outfile, ";\n");

		switch (stg) {
		    case STGARG:
		    case STGLENG:
			/* Part of the argument list, don't write them out
			   again */
			continue;	    /* Go back to top of the loop */
		    case STGBSS:
		    case STGEQUIV:
		    case STGCOMMON:
			nice_printf (outfile, "static ");
			break;
		    case STGEXT:
			nice_printf (outfile, "extern ");
			break;
		    case STGAUTO:
			break;
		    case STGINIT:
		    case STGUNKNOWN:
			/* Don't want to touch the initialized data, that will
			   be handled elsewhere.  Unknown data have
			   already been complained about, so skip them */
			continue;
		    default:
			erri("list_decls:  can't handle storage class %d",
				stg);
			continue;
		} /* switch */

		if (type == TYCHAR && halign && class != CLPROC
		&& ISICON(var->vleng)) {
			nice_printf(outfile, "struct { %s fill; char val",
				halign);
			x = wr_char_len(outfile, var->vdim,
				var->vleng->constblock.Const.ci, 1);
			if (x %= hsize)
				nice_printf(outfile, "; char fill2[%ld]",
					hsize - x);
			nice_printf(outfile, "; } %s_st;\n", var->cvarname);
			def_start(outfile, var->cvarname, CNULL, var->cvarname);
			ind_printf(0, outfile, "_st.val\n");
			last_type = -1;
			write_header = 2;
			continue;
			}
		nice_printf(outfile, "%s ",
			c_type_decl(type, class == CLPROC));
	    } /* else */

/* Character type is really a string type.  Put out a '*' for variable
   length strings, and also for equivalences */

	    if (type == TYCHAR && class != CLPROC
		    && (!var->vleng || !ISICON (var -> vleng))
	    || oneof_stg(var, stg, M(STGEQUIV)|M(STGCOMMON)))
		nice_printf (outfile, "*%s", var->cvarname);
	    else {
		nice_printf (outfile, "%s", var->cvarname);
		if (class == CLPROC)
			proto(outfile, var->arginfo, var->fvarname);
		else if (type == TYCHAR && ISICON ((var -> vleng)))
			wr_char_len(outfile, var->vdim,
				(int)var->vleng->constblock.Const.ci, 0);
		else if (var -> vdim &&
		    !oneof_stg (var, stg, M(STGEQUIV)|M(STGCOMMON)))
			comment = wr_ardecls(outfile, var->vdim, 1L);
		}

	    if (comment)
		nice_printf (outfile, "%s", comment);
 Alias1:
	    if (Alias) {
		char *amp, *lp, *name, *rp;
		char *equiv_name ();
		ftnint voff = var -> voffset;
		int et0, expr_type, k;
		Extsym *E;
		struct Equivblock *eb;
		char buf[16];

/* We DON'T want to use oneof_stg here, because we need to distinguish
   between them */

		if (stg == STGEQUIV) {
			name = equiv_name(k = var->vardesc.varno, CNULL);
			eb = eqvclass + k;
			if (eb->eqvinit) {
				amp = "&";
				et0 = TYERROR;
				}
			else {
				amp = "";
				et0 = eb->eqvtype;
				}
			expr_type = et0;
		    }
		else {
			E = &extsymtab[var->vardesc.varno];
			sprintf(name = buf, "%s%d", E->cextname, E->curno);
			expr_type = type;
			et0 = -1;
			amp = "&";
		} /* else */

		if (!Define)
			nice_printf (outfile, " = ");
		if (voff) {
			k = typesize[type];
			switch((int)(voff % k)) {
				case 0:
					voff /= k;
					expr_type = type;
					break;
				case SZSHORT:
				case SZSHORT+SZLONG:
					expr_type = TYSHORT;
					voff /= SZSHORT;
					break;
				case SZLONG:
					expr_type = TYLONG;
					voff /= SZLONG;
					break;
				default:
					expr_type = TYCHAR;
				}
			}

		if (expr_type == type) {
			lp = rp = "";
			if (et0 == -1 && !voff)
				goto cast;
			}
		else {
			lp = "(";
			rp = ")";
 cast:
			nice_printf(outfile, "(%s *)", c_type_decl(type, 0));
			}

/* Now worry about computing the offset */

		if (voff) {
		    if (expr_type == et0)
			nice_printf (outfile, "%s%s + %ld%s",
				lp, name, voff, rp);
		    else
			nice_printf(outfile, "%s(%s *)%s%s + %ld%s", lp,
				c_type_decl (expr_type, 0), amp,
				name, voff, rp);
		} else
		    nice_printf(outfile, "%s%s", amp, name);
/* Always put these at the end of the line */
		last_type = last_class = last_stg = -1;
		write_header = 0;
		if (Define) {
			ind_printf(0, outfile, ")\n");
			write_header = 2;
			}
		continue;
		}
	    write_header = 0;
	    last_type = type;
	    last_class = class;
	    last_stg = stg;
	} /* if (var) */
    } /* for (entry = hashtab */

    if (!write_header)
	nice_printf (outfile, ";\n\n");
    else if (write_header == 2)
	nice_printf(outfile, "\n");

/* Next, namelists, which may reference equivs */

    if (namelists) {
	write_namelists(namelists = revchain(namelists), outfile);
	frchain(&namelists);
	}

/* Finally, ioblocks (which may reference equivs and namelists) */
    if (iob_list)
	write_ioblocks(outfile);
    if (assigned_fmts)
	write_assigned_fmts(outfile);
    lineno = lineno_save;
} /* list_decls */

do_uninit_equivs (outfile, did_one)
FILE *outfile;
int *did_one;
{
    extern int nequiv;
    struct Equivblock *eqv, *lasteqv = eqvclass + nequiv;
    int k, last_type = -1, t;

    for (eqv = eqvclass; eqv < lasteqv; eqv++)
	if (!eqv -> eqvinit && eqv -> eqvtop != eqv -> eqvbottom) {
	    if (!*did_one)
		nice_printf (outfile, "/* System generated locals */\n");
	    t = eqv->eqvtype;
	    if (last_type == t)
		nice_printf (outfile, ", ");
	    else {
		if (*did_one)
		    nice_printf (outfile, ";\n");
		nice_printf (outfile, "static %s ", c_type_decl(t, 0));
		k = typesize[t];
	    } /* else */
	    nice_printf(outfile, "%s", equiv_name((int)(eqv - eqvclass), CNULL));
	    nice_printf(outfile, "[%ld]",
		(eqv->eqvtop - eqv->eqvbottom + k - 1) / k);
	    last_type = t;
	    *did_one = 1;
	} /* if !eqv -> eqvinit */
} /* do_uninit_equivs */


/* wr_ardecls -- Writes the brackets and size for an array
   declaration.  Because of the inner workings of the compiler,
   multi-dimensional arrays get mapped directly into a one-dimensional
   array, so we have to compute the size of the array here.  When the
   dimension is greater than 1, a string comment about the original size
   is returned */

char *wr_ardecls(outfile, dimp, size)
FILE *outfile;
struct Dimblock *dimp;
long size;
{
    int i, k;
    static char buf[1000];

    if (dimp == (struct Dimblock *) NULL)
	return NULL;

    sprintf(buf, "\t/* was ");	/* would like to say  k = sprintf(...), but */
    k = strlen(buf);		/* BSD doesn't return char transmitted count */

    for (i = 0; i < dimp -> ndim; i++) {
	expptr this_size = dimp -> dims[i].dimsize;

	if (!ISICON (this_size))
	    err ("wr_ardecls:  nonconstant array size");
	else {
	    size *= this_size -> constblock.Const.ci;
	    sprintf(buf+k, "[%ld]", this_size -> constblock.Const.ci);
	    k += strlen(buf+k);	/* BSD prevents combining this with prev stmt */
	} /* else */
    } /* for i = 0 */

    nice_printf (outfile, "[%ld]", size);
    strcat(buf+k, " */");

    return (i > 1) ? buf : NULL;
} /* wr_ardecls */



/* ----------------------------------------------------------------------

	The following routines read from the p1 intermediate file.  If
   that format changes, only these routines need be changed

   ---------------------------------------------------------------------- */

static int get_p1_token (infile)
FILE *infile;
{
    int token = P1_UNKNOWN;

/* NOT PORTABLE!! */

    if (fscanf (infile, "%d", &token) == EOF)
	return P1_EOF;

/* Skip over the ": " */

    if (getc (infile) != '\n')
	getc (infile);

    return token;
} /* get_p1_token */



/* Returns a (null terminated) string from the input file */

static int p1gets (fp, str, size)
FILE *fp;
char *str;
int size;
{
    char *fgets ();
    char c;

    if (str == NULL)
	return 0;

    if ((c = getc (fp)) != ' ')
	ungetc (c, fp);

    if (fgets (str, size, fp)) {
	int length;

	str[size - 1] = '\0';
	length = strlen (str);

/* Get rid of the newline */

	if (str[length - 1] == '\n')
	    str[length - 1] = '\0';
	return 1;

    } else if (feof (fp))
	return EOF;
    else
	return 0;
} /* p1gets */


static int p1get_const (infile, type, resultp)
FILE *infile;
int type;
struct Constblock **resultp;
{
    int status;
    struct Constblock *result;

	if (type != TYCHAR) {
		*resultp = result = ALLOC(Constblock);
		result -> tag = TCONST;
		result -> vtype = type;
		}

    switch (type) {
        case TYSHORT:
	case TYLONG:
	case TYLOGICAL:
	    status = p1getd (infile, &(result -> Const.ci));
	    break;
	case TYREAL:
	case TYDREAL:
	    status = p1getf(infile, &result->Const.cds[0]);
	    result->vstg = 1;
	    break;
	case TYCOMPLEX:
	case TYDCOMPLEX:
	    status = p1getf(infile, &result->Const.cds[0]);
	    if (status && status != EOF)
		status = p1getf(infile, &result->Const.cds[1]);
	    result->vstg = 1;
	    break;
	case TYCHAR:
	    status = fscanf(infile, "%lx", resultp);
	    break;
	default:
	    erri ("p1get_const:  bad constant type '%d'", type);
	    status = 0;
	    break;
    } /* switch */

    return status;
} /* p1get_const */

static int p1getd (infile, result)
FILE *infile;
long *result;
{
    return fscanf (infile, "%ld", result);
} /* p1getd */

 static int
p1getf(infile, result)
 FILE *infile;
 char **result;
{

	char buf[1324];
	register int k;

	k = fscanf (infile, "%s", buf);
	if (k < 1)
		k = EOF;
	else
		strcpy(*result = mem(strlen(buf)+1,0), buf);
	return k;
}

static int p1getn (infile, count, result)
FILE *infile;
int count;
char **result;
{

    char *bufptr;
    extern ptr ckalloc ();

    bufptr = (char *) ckalloc (count);

    if (result)
	*result = bufptr;

    for (; !feof (infile) && count > 0; count--)
	*bufptr++ = getc (infile);

    return feof (infile) ? EOF : 1;
} /* p1getn */

 static void
proto(outfile, at, fname)
 FILE *outfile;
 Argtypes *at;
 char *fname;
{
	int i, j, k, n;
	char *comma;
	Atype *atypes;
	Namep np;
	chainp cp;
	extern void bad_atypes();

	if (at) {
		/* Correct types that we learn on the fly, e.g.
			subroutine gotcha(foo)
			external foo
			call zap(...,foo,...)
			call foo(...)
		*/
		atypes = at->atypes;
		n = at->nargs;
		for(i = 0; i++ < n; atypes++) {
			if (!(cp = atypes->cp))
				continue;
			j = atypes->type;
			do {
				np = (Namep)cp->datap;
				k = np->vtype;
				if (np->vclass == CLPROC) {
					if (!np->vimpltype && k)
						k += 200;
					else {
						if (j >= 300)
							j = TYUNKNOWN + 200;
						continue;
						}
					}
				if (j == k)
					continue;
				if (j >= 300
				||  j == 200 && k >= 200)
					j = k;
				else {
					bad_atypes(at,fname,i,j,k,""," and");
					goto break2;
					}
				}
				while(cp = cp->nextp);
			atypes->type = j;
			frchain(&atypes->cp);
			}
		}
 break2:
	if (parens) {
		nice_printf(outfile, parens);
		return;
		}

	if (!at || (n = at->nargs) < 0) {
		nice_printf(outfile, Ansi == 1 ? "()" : "(...)");
		return;
		}

	if (n == 0) {
		nice_printf(outfile, Ansi == 1 ? "(void)" : "()");
		return;
		}

	atypes = at->atypes;
	nice_printf(outfile, "(");
	comma = "";
	for(; --n >= 0; atypes++) {
		k = atypes->type;
		if (k == TYADDR)
			nice_printf(outfile, "%schar **", comma);
		else if (k >= 200) {
			k -= 200;
			nice_printf(outfile, "%s%s", comma,
				usedcasts[k] = casttypes[k]);
			}
		else if (k >= 100)
			nice_printf(outfile,
					k == TYCHAR + 100 ? "%s%s *" : "%s%s",
					comma, c_type_decl(k-100, 0));
		else
			nice_printf(outfile, "%s%s *", comma,
					c_type_decl(k, 0));
		comma = ", ";
		}
	nice_printf(outfile, ")");
	}

 void
protowrite(protofile, type, name, e, lengths)
 FILE *protofile;
 char *name;
 struct Entrypoint *e;
 chainp lengths;
{
	extern char used_rets[];

	nice_printf(protofile, "extern %s %s", protorettypes[type], name);
	list_arg_types(protofile, e, lengths, 0, ";\n");
	used_rets[type] = 1;
	}

 static void
do_p1_1while(outfile)
 FILE *outfile;
{
	if (*wh_next) {
		nice_printf(outfile,
			"for(;;) { /* while(complicated condition) */\n" /*}*/ );
		next_tab(outfile);
		}
	else
		nice_printf(outfile, "while(" /*)*/ );
	}

 static void
do_p1_2while(infile, outfile)
 FILE *infile, *outfile;
{
	expptr test;

	test = do_format(infile, outfile);
	if (*wh_next)
		nice_printf(outfile, "if (!(");
	expr_out(outfile, test);
	if (*wh_next++)
		nice_printf(outfile, "))\n\tbreak;\n");
	else {
		nice_printf(outfile, /*(*/ ") {\n");
		next_tab(outfile);
		}
	}

 static void
do_p1_elseifstart(outfile)
 FILE *outfile;
{
	if (*ei_next++) {
		prev_tab(outfile);
		nice_printf(outfile, /*{*/
			"} else /* if(complicated condition) */ {\n" /*}*/ );
		next_tab(outfile);
		}
	}
