/****************************************************************
Copyright 1990 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.
****************************************************************/

#include "defs.h"

int oneof_stg (name, stg, mask)
 Namep name;
 int stg, mask;
{
	if (stg == STGCOMMON && name) {
		if ((mask & M(STGEQUIV)))
			return name->vcommequiv;
		if ((mask & M(STGCOMMON)))
			return !name->vcommequiv;
		}
	return ONEOF(stg, mask);
	}


/* op_assign -- given a binary opcode, return the associated assignment
   operator */

int op_assign (opcode)
int opcode;
{
    int retval = -1;

    switch (opcode) {
        case OPPLUS: retval = OPPLUSEQ; break;
	case OPMINUS: retval = OPMINUSEQ; break;
	case OPSTAR: retval = OPSTAREQ; break;
	case OPSLASH: retval = OPSLASHEQ; break;
	case OPMOD: retval = OPMODEQ; break;
	case OPLSHIFT: retval = OPLSHIFTEQ; break;
	case OPRSHIFT: retval = OPRSHIFTEQ; break;
	case OPBITAND: retval = OPBITANDEQ; break;
	case OPBITXOR: retval = OPBITXOREQ; break;
	case OPBITOR: retval = OPBITOREQ; break;
	default:
	    erri ("op_assign:  bad opcode '%d'", opcode);
	    break;
    } /* switch */

    return retval;
} /* op_assign */


 char *
Alloc(n)	/* error-checking version of malloc */
		/* ckalloc initializes memory to 0; Alloc does not */
 int n;
{
	char errbuf[32];
	register char *rv;

	rv = malloc(n);
	if (!rv) {
		sprintf(errbuf, "malloc(%d) failure!", n);
		Fatal(errbuf);
		}
	return rv;
	}


cpn(n, a, b)
register int n;
register char *a, *b;
{
	while(--n >= 0)
		*b++ = *a++;
}



eqn(n, a, b)
register int n;
register char *a, *b;
{
	while(--n >= 0)
		if(*a++ != *b++)
			return(NO);
	return(YES);
}







cmpstr(a, b, la, lb)	/* compare two strings */
register char *a, *b;
ftnint la, lb;
{
	register char *aend, *bend;
	aend = a + la;
	bend = b + lb;


	if(la <= lb)
	{
		while(a < aend)
			if(*a != *b)
				return( *a - *b );
			else
			{
				++a;
				++b;
			}

		while(b < bend)
			if(*b != ' ')
				return(' ' - *b);
			else
				++b;
	}

	else
	{
		while(b < bend)
			if(*a != *b)
				return( *a - *b );
			else
			{
				++a;
				++b;
			}
		while(a < aend)
			if(*a != ' ')
				return(*a - ' ');
			else
				++a;
	}
	return(0);
}


/* hookup -- Same as LISP NCONC, that is a destructive append of two lists */

chainp hookup(x,y)
register chainp x, y;
{
	register chainp p;

	if(x == NULL)
		return(y);

	for(p = x ; p->nextp ; p = p->nextp)
		;
	p->nextp = y;
	return(x);
}



struct Listblock *mklist(p)
chainp p;
{
	register struct Listblock *q;

	q = ALLOC(Listblock);
	q->tag = TLIST;
	q->listp = p;
	return(q);
}


chainp mkchain(p,q)
register char * p;
register chainp q;
{
	register chainp r;

	if(chains)
	{
		r = chains;
		chains = chains->nextp;
	}
	else
		r = ALLOC(Chain);

	r->datap = p;
	r->nextp = q;
	return(r);
}

 chainp
revchain(next)
 register chainp next;
{
	register chainp p, prev = 0;

	while(p = next) {
		next = p->nextp;
		p->nextp = prev;
		prev = p;
		}
	return prev;
	}


/* addunder -- turn a cvarname into an external name */
/* The cvarname may already end in _ (to avoid C keywords); */
/* if not, it has room for appending an _. */

 char *
addunder(s)
 register char *s;
{
	register int c, i;
	char *s0 = s;

	i = 0;
	while(c = *s++)
		if (c == '_')
			i++;
		else
			i = 0;
	if (!i) {
		*s-- = 0;
		*s = '_';
		}
	return( s0 );
	}


/* copyn -- return a new copy of the input Fortran-string */

char *copyn(n, s)
register int n;
register char *s;
{
	register char *p, *q;

	p = q = (char *) Alloc(n);
	while(--n >= 0)
		*q++ = *s++;
	return(p);
}



/* copys -- return a new copy of the input C-string */

char *copys(s)
char *s;
{
	return( copyn( strlen(s)+1 , s) );
}



/* convci -- Convert Fortran-string to integer; assumes that input is a
   legal number, with no trailing blanks */

ftnint convci(n, s)
register int n;
register char *s;
{
	ftnint sum;
	sum = 0;
	while(n-- > 0)
		sum = 10*sum + (*s++ - '0');
	return(sum);
}

/* convic - Convert Integer constant to string */

char *convic(n)
ftnint n;
{
	static char s[20];
	register char *t;

	s[19] = '\0';
	t = s+19;

	do	{
		*--t = '0' + n%10;
		n /= 10;
	} while(n > 0);

	return(t);
}



/* mkname -- add a new identifier to the environment, including the closed
   hash table. */

Namep mkname(s)
register char *s;
{
	struct Hashentry *hp;
	register Namep q;
	register int c, hash, i;
	register char *t;
	char *s0;
	char errbuf[64];

	hash = i = 0;
	s0 = s;
	while(c = *s++) {
		hash += c;
		if (c == '_')
			i = 1;
		}
	hash %= maxhash;

/* Add the name to the closed hash table */

	hp = hashtab + hash;

	while(q = hp->varp)
		if( hash == hp->hashval && !strcmp(s0,q->fvarname) )
			return(q);
		else if(++hp >= lasthash)
			hp = hashtab;

	if(++nintnames >= maxhash-1)
		many("names", 'n', maxhash);	/* Fatal error */
	hp->varp = q = ALLOC(Nameblock);
	hp->hashval = hash;
	q->tag = TNAME;	/* TNAME means the tag type is NAME */
	c = s - s0;
	if (c > 7 && noextflag) {
		sprintf(errbuf, "\"%.35s%s\" over 6 characters long", s0,
			c > 36 ? "..." : "");
		errext(errbuf);
		}
	q->fvarname = strcpy(mem(c,0), s0);
	t = q->cvarname = mem(c + i + 1, 0);
	s = s0;
	/* add __ to the end of any name containing _ */
	while(*t = *s++)
		t++;
	if (i) {
		t[0] = t[1] = '_';
		t[2] = 0;
		}
	else if (in_vector(s0) >= 0) {
		t[0] = '_';
		t[1] = 0;
		}
	return(q);
}


struct Labelblock *mklabel(l)
ftnint l;
{
	register struct Labelblock *lp;

	if(l <= 0)
		return(NULL);

	for(lp = labeltab ; lp < highlabtab ; ++lp)
		if(lp->stateno == l)
			return(lp);

	if(++highlabtab > labtabend)
		many("statement labels", 's', maxstno);

	lp->stateno = l;
	lp->labelno = newlabel();
	lp->blklevel = 0;
	lp->labused = NO;
	lp->fmtlabused = NO;
	lp->labdefined = NO;
	lp->labinacc = NO;
	lp->labtype = LABUNKNOWN;
	lp->fmtstring = 0;
	return(lp);
}


newlabel()
{
	return( ++lastlabno );
}


/* this label appears in a branch context */

struct Labelblock *execlab(stateno)
ftnint stateno;
{
	register struct Labelblock *lp;

	if(lp = mklabel(stateno))
	{
		if(lp->labinacc)
			warn1("illegal branch to inner block, statement label %s",
			    convic(stateno) );
		else if(lp->labdefined == NO)
			lp->blklevel = blklevel;
		if(lp->labtype == LABFORMAT)
			err("may not branch to a format");
		else
			lp->labtype = LABEXEC;
	}
	else
		execerr("illegal label %s", convic(stateno));

	return(lp);
}


/* find or put a name in the external symbol table */

Extsym *mkext(f,s)
char *f, *s;
{
	Extsym *p;

	for(p = extsymtab ; p<nextext ; ++p)
		if(!strcmp(s,p->cextname))
			return( p );

	if(nextext >= lastext)
		many("external symbols", 'x', maxext);

	nextext->fextname = strcpy(gmem(strlen(f)+1,0), f);
	nextext->cextname = f == s
				? nextext->fextname
				: strcpy(gmem(strlen(s)+1,0), s);
	nextext->extstg = STGUNKNOWN;
	nextext->extp = 0;
	nextext->allextp = 0;
	nextext->extleng = 0;
	nextext->maxleng = 0;
	nextext->extinit = 0;
	nextext->curno = nextext->maxno = 0;
	return( nextext++ );
}


Addrp builtin(t, s, dbi)
int t, dbi;
char *s;
{
	register Extsym *p;
	register Addrp q;
	extern chainp used_builtins;

	p = mkext(s,s);
	if(p->extstg == STGUNKNOWN)
		p->extstg = STGEXT;
	else if(p->extstg != STGEXT)
	{
		errstr("improper use of builtin %s", s);
		return(0);
	}

	q = ALLOC(Addrblock);
	q->tag = TADDR;
	q->vtype = t;
	q->vclass = CLPROC;
	q->vstg = STGEXT;
	q->memno = p - extsymtab;
	q->dbl_builtin = dbi;

/* A NULL pointer here tells you to use   memno   to check the external
   symbol table */

	q -> uname_tag = UNAM_EXTERN;

/* Add to the list of used builtins */

	if (dbi >= 0)
		add_extern_to_list (q, &used_builtins);
	return(q);
}



add_extern_to_list (addr, list_store)
Addrp addr;
chainp *list_store;
{
    chainp last = CHNULL;
    chainp list;
    int memno;

    if (list_store == (chainp *) NULL || addr == (Addrp) NULL)
	return;

    list = *list_store;
    memno = addr -> memno;

    for (;list; last = list, list = list -> nextp) {
	Addrp this = (Addrp) (list -> datap);

	if (this -> tag == TADDR && this -> uname_tag == UNAM_EXTERN &&
		this -> memno == memno)
	    return;
    } /* for */

    if (*list_store == CHNULL)
	*list_store = mkchain((char *)cpexpr((expptr)addr), CHNULL);
    else
	last->nextp = mkchain((char *)cpexpr((expptr)addr), CHNULL);

} /* add_extern_to_list */


frchain(p)
register chainp *p;
{
	register chainp q;

	if(p==0 || *p==0)
		return;

	for(q = *p; q->nextp ; q = q->nextp)
		;
	q->nextp = chains;
	chains = *p;
	*p = 0;
}

 void
frexchain(p)
 register chainp *p;
{
	register chainp q, r;

	if (q = *p) {
		for(;;q = r) {
			frexpr((expptr)q->datap);
			if (!(r = q->nextp))
				break;
			}
		q->nextp = chains;
		chains = *p;
		*p = 0;
		}
	}


tagptr cpblock(n,p)
register int n;
register char * p;
{
	register ptr q;

	memcpy((char *)(q = ckalloc(n)), (char *)p, n);
	return( (tagptr) q);
}



ftnint lmax(a, b)
ftnint a, b;
{
	return( a>b ? a : b);
}

ftnint lmin(a, b)
ftnint a, b;
{
	return(a < b ? a : b);
}




maxtype(t1, t2)
int t1, t2;
{
	int t;

	t = t1 >= t2 ? t1 : t2;
	if(t==TYCOMPLEX && (t1==TYDREAL || t2==TYDREAL) )
		t = TYDCOMPLEX;
	return(t);
}



/* return log base 2 of n if n a power of 2; otherwise -1 */
log_2(n)
ftnint n;
{
	int k;

	/* trick based on binary representation */

	if(n<=0 || (n & (n-1))!=0)
		return(-1);

	for(k = 0 ;  n >>= 1  ; ++k)
		;
	return(k);
}



frrpl()
{
	struct Rplblock *rp;

	while(rpllist)
	{
		rp = rpllist->rplnextp;
		free( (charptr) rpllist);
		rpllist = rp;
	}
}



/* Call a Fortran function with an arbitrary list of arguments */

int callk_kludge;

expptr callk(type, name, args)
int type;
char *name;
chainp args;
{
	register expptr p;

	p = mkexpr(OPCALL,
		(expptr)builtin(callk_kludge ? callk_kludge : type, name, 0),
		(expptr)args);
	p->exprblock.vtype = type;
	return(p);
}



expptr call4(type, name, arg1, arg2, arg3, arg4)
int type;
char *name;
expptr arg1, arg2, arg3, arg4;
{
	struct Listblock *args;
	args = mklist( mkchain((char *)arg1,
			mkchain((char *)arg2,
				mkchain((char *)arg3,
	    				mkchain((char *)arg4, CHNULL)) ) ) );
	return( callk(type, name, (chainp)args) );
}




expptr call3(type, name, arg1, arg2, arg3)
int type;
char *name;
expptr arg1, arg2, arg3;
{
	struct Listblock *args;
	args = mklist( mkchain((char *)arg1,
			mkchain((char *)arg2,
				mkchain((char *)arg3, CHNULL) ) ) );
	return( callk(type, name, (chainp)args) );
}





expptr call2(type, name, arg1, arg2)
int type;
char *name;
expptr arg1, arg2;
{
	struct Listblock *args;

	args = mklist( mkchain((char *)arg1, mkchain((char *)arg2, CHNULL) ) );
	return( callk(type,name, (chainp)args) );
}




expptr call1(type, name, arg)
int type;
char *name;
expptr arg;
{
	return( callk(type,name, (chainp)mklist(mkchain((char *)arg,CHNULL)) ));
}


expptr call0(type, name)
int type;
char *name;
{
	return( callk(type, name, CHNULL) );
}



struct Impldoblock *mkiodo(dospec, list)
chainp dospec, list;
{
	register struct Impldoblock *q;

	q = ALLOC(Impldoblock);
	q->tag = TIMPLDO;
	q->impdospec = dospec;
	q->datalist = list;
	return(q);
}




/* ckalloc -- Allocate 1 memory unit of size   n,   checking for out of
   memory error */

ptr ckalloc(n)
register int n;
{
	register ptr p;
	if( p = (ptr)calloc(1, (unsigned) n) )
		return(p);
	fprintf(stderr, "failing to get %d bytes\n",n);
	Fatal("out of memory");
	/* NOT REACHED */ return 0;
}



isaddr(p)
register expptr p;
{
	if(p->tag == TADDR)
		return(YES);
	if(p->tag == TEXPR)
		switch(p->exprblock.opcode)
		{
		case OPCOMMA:
			return( isaddr(p->exprblock.rightp) );

		case OPASSIGN:
		case OPASSIGNI:
		case OPPLUSEQ:
		case OPMINUSEQ:
		case OPSLASHEQ:
		case OPMODEQ:
		case OPLSHIFTEQ:
		case OPRSHIFTEQ:
		case OPBITANDEQ:
		case OPBITXOREQ:
		case OPBITOREQ:
			return( isaddr(p->exprblock.leftp) );
		}
	return(NO);
}




isstatic(p)
register expptr p;
{
	extern int useauto;
	if(p->headblock.vleng && !ISCONST(p->headblock.vleng))
		return(NO);

	switch(p->tag)
	{
	case TCONST:
		return(YES);

	case TADDR:
		if(ONEOF(p->addrblock.vstg,MSKSTATIC) &&
		    ISCONST(p->addrblock.memoffset) && !useauto)
			return(YES);

	default:
		return(NO);
	}
}



/* addressable -- return True iff it is a constant value, or can be
   referenced by constant values */

addressable(p)
register expptr p;
{
	switch(p->tag)
	{
	case TCONST:
		return(YES);

	case TADDR:
		return( addressable(p->addrblock.memoffset) );

	default:
		return(NO);
	}
}


/* isnegative_const -- returns true if the constant is negative.  Returns
   false for imaginary and nonnumeric constants */

int isnegative_const (cp)
struct Constblock *cp;
{
    int retval;

    if (cp == NULL)
	return 0;

    switch (cp -> vtype) {
        case TYSHORT:
	case TYLONG:
	    retval = cp -> Const.ci < 0;
	    break;
	case TYREAL:
	case TYDREAL:
		retval = cp->vstg ? *cp->Const.cds[0] == '-'
				  :  cp->Const.cd[0] < 0.0;
	    break;
	default:

	    retval = 0;
	    break;
    } /* switch */

    return retval;
} /* isnegative_const */

negate_const(cp)
 Constp cp;
{
    if (cp == (struct Constblock *) NULL)
	return;

    switch (cp -> vtype) {
	case TYSHORT:
	case TYLONG:
	    cp -> Const.ci = - cp -> Const.ci;
	    break;
	case TYCOMPLEX:
	case TYDCOMPLEX:
		if (cp->vstg)
		    switch(*cp->Const.cds[1]) {
			case '-':
				++cp->Const.cds[1];
				break;
			case '0':
				break;
			default:
				--cp->Const.cds[1];
			}
		else
	    		cp->Const.cd[1] = -cp->Const.cd[1];
		/* no break */
	case TYREAL:
	case TYDREAL:
		if (cp->vstg)
		    switch(*cp->Const.cds[0]) {
			case '-':
				++cp->Const.cds[0];
				break;
			case '0':
				break;
			default:
				--cp->Const.cds[0];
			}
		else
	    		cp->Const.cd[0] = -cp->Const.cd[0];
	    break;
	case TYCHAR:
	case TYLOGICAL:
	    erri ("negate_const:  can't negate type '%d'", cp -> vtype);
	    break;
	default:
	    erri ("negate_const:  bad type '%d'",
		    cp -> vtype);
	    break;
    } /* switch */
} /* negate_const */

ffilecopy (infp, outfp)
FILE *infp, *outfp;
{
    while (!feof (infp)) {
	register c = getc (infp);
	if (!feof (infp))
	putc (c, outfp);
    } /* while */
} /* ffilecopy */


#define NOT_IN_VECTOR -1

/* in_vector -- verifies whether   str   is in c_keywords.
   If so, the index is returned else   NOT_IN_VECTOR   is returned.
   c_keywords must be in alphabetical order (as defined by strcmp).
*/

int in_vector(str)
char *str;
{
	extern int n_keywords;
	extern char *c_keywords[];
	register int n = n_keywords;
	register char **K = c_keywords;
	register int n1, t;

	do {
		n1 = n >> 1;
		if (!(t = strcmp(str, K[n1])))
			return K - c_keywords + n1;
		if (t < 0)
			n = n1;
		else {
			n -= ++n1;
			K += n1;
			}
		}
		while(n > 0);

	return NOT_IN_VECTOR;
	} /* in_vector */


int is_negatable (Const)
Constp Const;
{
    int retval = 0;
    if (Const != (Constp) NULL)
	switch (Const -> vtype) {
	    case TYSHORT:
	        retval = Const -> Const.ci >= -BIGGEST_SHORT;
	        break;
	    case TYLONG:
	        retval = Const -> Const.ci >= -BIGGEST_LONG;
	        break;
	    case TYREAL:
	    case TYDREAL:
	    case TYCOMPLEX:
	    case TYDCOMPLEX:
	        retval = 1;
	        break;
	    case TYLOGICAL:
	    case TYCHAR:
	    case TYSUBR:
	    default:
	        retval = 0;
	        break;
	} /* switch */

    return retval;
} /* is_negatable */

backup(fname, bname)
 char *fname, *bname;
{
	FILE *b, *f;
	static char couldnt[] = "Couldn't open %.80s";

	if (!(f = fopen(fname, binread))) {
		warn1(couldnt, fname);
		return;
		}
	if (!(b = fopen(bname, binwrite))) {
		warn1(couldnt, bname);
		return;
		}
	ffilecopy(f, b);
	fclose(f);
	fclose(b);
	}


/* struct_eq -- returns YES if structures have the same field names and
   types, NO otherwise */

int struct_eq (s1, s2)
chainp s1, s2;
{
    struct Dimblock *d1, *d2;
    Constp cp1, cp2;

    if (s1 == CHNULL && s2 == CHNULL)
	return YES;
    for(; s1 && s2; s1 = s1->nextp, s2 = s2->nextp) {
	register Namep v1 = (Namep) s1 -> datap;
	register Namep v2 = (Namep) s2 -> datap;

	if (v1 == (Namep) NULL || v1 -> tag != TNAME ||
		v2 == (Namep) NULL || v2 -> tag != TNAME)
	    return NO;

	if (v1->vtype != v2->vtype || v1->vclass != v2->vclass
		|| strcmp(v1->fvarname, v2->fvarname))
	    return NO;

	/* compare dimensions (needed for comparing COMMON blocks) */

	if (d1 = v1->vdim) {
		if (!(cp1 = (Constp)d1->nelt) || cp1->tag != TCONST)
			return NO;
		if (!(d2 = v2->vdim))
			if (cp1->Const.ci == 1)
				continue;
			else
				return NO;
		if (!(cp2 = (Constp)d2->nelt) || cp2->tag != TCONST
		||  cp1->Const.ci != cp2->Const.ci)
			return NO;
		}
	else if ((d2 = v2->vdim) && (!(cp2 = (Constp)d2->nelt)
				|| cp2->tag != TCONST
				|| cp2->Const.ci != 1))
		return NO;
    } /* while s1 != CHNULL && s2 != CHNULL */

    return s1 == CHNULL && s2 == CHNULL;
} /* struct_eq */
