#include <ctype.h>

#include "defs"

static int lastfmtchar;
static int writeop;
static int needcomma;


ptr mkiost(kwd,unit,list)
int kwd;
ptr unit;
ptr list;
{
register ptr p;

if(unit!=NULL && unit->vtype!=TYINT)
	{
	execerr("I/O unit must be an integer", "");
	return(NULL);
	}
p = allexpblock();
p->tag = TIOSTAT;
p->vtype = TYINT;
p->iokwd = kwd;
p->iounit = unit;
p->iolist = list;

return(p);
}




struct iogroup *mkiogroup(list, format, dop)
ptr list;
char *format;
ptr dop;
{
register struct iogroup *p;

p = ALLOC(iogroup);
p->tag = TIOGROUP;
p->doptr = (struct doblock *)dop;
p->iofmt = format;
p->ioitems = list;
return(p);
}

ptr exio(iostp, errhandle)
struct iostblock *iostp;
int errhandle;
{
ptr unit, list;
int fmtlabel, errlabel, endlabel, jumplabel;
ptr errval;
int fmtio;

if(iostp == NULL)
	return( errnode() );
unit = iostp->iounit;
list = iostp->iolist;

/* kwd=	0  binary input 	2  formatted input
	1  binary output	3  formatted output
*/

writeop = iostp->iokwd & 01;
if( fmtio = (iostp->iokwd & 02) )
	fmtlabel = nextlab() ;
frexpblock(iostp);

errval = 0;
endlabel = 0;
if(errhandle)
	{
	switch(tailor.errmode)
		{
		default:
			execerr("no error handling ", "");
			return( errnode() );

		case IOERRIBM:	/* ibm: err=, end= */
			jumplabel = nextlab();
			break;

		case IOERRFORT77:	/* New Fortran Standard: iostat= */
			break;

		}
	errval = gent(TYINT, PNULL);
	}
if(unit)
	unit = simple(RVAL, unit);
else	unit = mkint(writeop ? tailor.ftnout : tailor.ftnin);

if(unit->tag!=TCONST && (unit->tag!=TNAME || unit->vsubs!=0))
	unit = simple(LVAL, mknode(TASGNOP,OPASGN,gent(TYINT,PNULL),unit));

simlist(list);

exlab(0);
putic(ICKEYWORD, (writeop ? FWRITE : FREAD) );
putic(ICOP, OPLPAR);
prexpr(unit);
frexpr(unit);

if( fmtio )
	{
	putic(ICOP, OPCOMMA);
	putic(ICLABEL, fmtlabel);
	}

if(errhandle) switch(tailor.errmode)
	{
	case IOERRIBM:
		putic(ICOP,OPCOMMA);
		putsii(ICCONST, "err =");
		putic(ICLABEL, errlabel = nextlab() );
		if(!writeop)
			{
			putic(ICOP,OPCOMMA);
			putsii(ICCONST, "end =");
			putic(ICLABEL, endlabel = nextlab() );
			}
		break;

	case IOERRFORT77:
		putic(ICOP,OPCOMMA);
		putsii(ICCONST, "iostat =");
		putname(errval);
		break;
	}

putic(ICOP,OPRPAR);
putic(ICBLANK, 1);

needcomma = NO;
doiolist(list);
if(fmtio)
	{
	exlab(fmtlabel);
	putic(ICKEYWORD, FFORMAT);
	putic(ICOP, OPLPAR);
	lastfmtchar = '(';
	doformat(1, list);
	putic(ICOP, OPRPAR);
	}
friolist(list);

if(errhandle && tailor.errmode==IOERRIBM)
	{
	exasgn(cpexpr(errval), OPASGN, mkint(0) );
	exgoto(jumplabel);
	exlab(errlabel);
	exasgn(cpexpr(errval), OPASGN, mkint(1) );
	if(endlabel)
		{
		exgoto(jumplabel);
		exlab(endlabel);
		exasgn(cpexpr(errval), OPASGN,
			mknode(TNEGOP,OPMINUS,mkint(1),PNULL) );
		}
	exlab(jumplabel);
	}

return( errval );
}

doiolist(list)
ptr list;
{
register ptr p, q;
register struct doblock *dop;
for(p = list ; p ; p = p->nextp)
	{
	switch( ((struct headbits *)(q = p->datap))->tag)
		{
		case TIOGROUP:
			if(dop = q->doptr)
				{
				if(needcomma)
					putic(ICOP, OPCOMMA);
				putic(ICOP, OPLPAR);
				needcomma = NO;
				}
			doiolist(q->ioitems);
			if(dop)
				{
				putic(ICOP,OPCOMMA);
				prexpr(dop->dovar);
				putic(ICOP, OPEQUALS);
				prexpr(dop->dopar[0]);
				putic(ICOP, OPCOMMA);
				prexpr(dop->dopar[1]);
				if(dop->dopar[2])
					{
					putic(ICOP, OPCOMMA);
					prexpr(dop->dopar[2]);
					}
				putic(ICOP, OPRPAR);
				needcomma = YES;
				}
			break;

		case TIOITEM:
			if(q->ioexpr)
				{
				if(needcomma)
					putic(ICOP, OPCOMMA);
				prexpr(q->ioexpr);
				needcomma = YES;
				}
			break;

		default:
			badtag("doiolist", q->tag);
		}
	}
}

doformat(nrep, list)
int nrep;
ptr list;
{
register ptr p, q;
int k;
ptr arrsize();

if(nrep > 1)
	{
	fmtnum(nrep);
	fmtop(OPLPAR);
	}

for(p = list ; p ; p = p->nextp)
	switch( ((struct headbits *)(q = p->datap))->tag)
		{
		case TIOGROUP:
			if(q->iofmt)
				prfmt(q->nrep, q->iofmt);
			else	{
				doformat(q->nrep>0 ? q->nrep :
					(q->doptr ? repfac(q->doptr) : 1),
					q->ioitems);
				}
			break;

		case TIOITEM:
			if(q->iofmt == NULL)
				break;

			if(q->nrep==0 && q->ioexpr && ((struct varblock *)q->ioexpr)->vdim)
				{
				if( ! isicon(arrsize(q->ioexpr), &k) )
					execerr("io of adjustable array", "");
				else
					prfmt(k, q->iofmt);
				}
			else
				prfmt(q->nrep, q->iofmt);
		}
if(nrep > 1)
	fmtop(OPRPAR);
}

fmtop(op)
register int op;
{
register c;

c = (op==OPLPAR ? '(' : (op==OPRPAR ? ')' : 'x') );
fmtcom(c);
putic(ICOP, op);
lastfmtchar = c;
}




fmtnum(k)
int k;
{
fmtcom('1');
prexpr( mkint(k) );
lastfmtchar = ',';	/* prevent further comma after factor*/
}








/* separate formats with comma unless already a slash*/
fmtcom(c)
int c;
{
if( c!='/' && c!=')' && lastfmtchar!='/' && lastfmtchar!='(' && lastfmtchar!=',' )
	{
	putic(ICOP, OPCOMMA);
	lastfmtchar = ',';
	}
}

prfmt(nrep, str)
int nrep;
char *str;
{
char fmt[20];
register int k, k0, k1, k2;
register char *t;

fmtcom(nrep>1 ? '1' : str[0]);

if(nrep > 1)
	{
	fmtnum(nrep);
	fmtop(OPLPAR);
	}

switch(str[0])
	{
	case 'd':
	case 'e':
	case 'g':
		if(writeop)
			{
			putsii(ICCONST, "1p");
			break;
			}
	
	case 'f':
		putsii(ICCONST, "0p");
		break;
		
	case 'c':
		k = convci(str+1);
		k0 = tailor.ftnchwd;
		k1 = k / k0;
		k2 = k % k0;
		if(k1>0 && k2>0)
			sprintf(fmt, "(%da%d,a%d)",k1,k0,k2);
		else if(k1>1)
			sprintf(fmt, "(%da%d)", k1, k0);
		else	sprintf(fmt, "a%d", k);
		putsii(ICCONST, fmt);
		lastfmtchar = 'f';	/* last char isnt operator */
		goto close;

	default:
		break;
	}
putsii(ICCONST,str);
/* if the format is an nH, act as if it ended with a non-operator character */
if( isdigit(str[0]) )
	{
	for(t = str+1 ; isdigit(*t) ; ++t);
		;
	if(*t=='h' || *t=='H')
		{
		lastfmtchar = 'f';
		goto close;
		}
	}
lastfmtchar = str[ strlen(str)-1 ];

close:
	if(nrep > 1)
		fmtop(OPRPAR);
}

friolist(list)
ptr list;
{
register ptr p, q;
register struct doblock *dop;

for(p = list; p; p = p->nextp)
	{
	switch ( ((struct headbits *)(q = p->datap))->tag)
		{
		case TIOGROUP:
			if(dop = q->doptr)
				{
				frexpr(dop->dovar);
				frexpr(dop->dopar[0]);
				frexpr(dop->dopar[1]);
				if(dop->dopar[2])
					frexpr(dop->dopar[2]);
				cfree(dop);
				}
			friolist(q->ioitems);
			break;

		case TIOITEM:
			if(q->ioexpr)
				frexpr(q->ioexpr);
			break;

		default:
			badtag("friolist", q->tag);
		}
	if(q->iofmt)
		cfree(q->iofmt);
	cfree(q);
	}
frchain( &list );
}

simlist(p)
register ptr p;
{
register ptr q, ep;
struct iogroup *enloop();

for( ; p ; p = p->nextp)
	switch( ((struct headbits *)(q = p->datap))->tag )
		{
		case TIOGROUP:
			simlist(q->ioitems);
			break;

		case TIOITEM:
			if(ep = q->ioexpr)
				{
				/* if element is a subaggregate, need
				   an implied do loop */
				if( (ep->voffset || ep->vsubs) &&
				    (ep->vdim || ep->vtypep) )
					p->datap = (int *)enloop(q);
				else
					q->ioexpr = simple(LVAL,ep);
				}
			break;

		default:
			badtag("ioblock", q->tag);
		}
}




/* replace an aggregate by an implied do loop of elements */

struct iogroup *enloop(p)
struct ioitem *p;
{
register struct doblock *dop;
struct iogroup *gp;
ptr np, q, v, arrsize(), mkioitem();
int nrep, k, nwd;

q = p->ioexpr;
np = arrsize(q);
if( ! isicon(np, &nrep) )
	nrep = 0;

if(q->vtype == TYCHAR)
	{
	nwd = ceil(conval(q->vtypep), tailor.ftnchwd);
	if(nwd != 1)
		np = simple(LVAL, mknode(TAROP,OPSTAR,np,mkint(nwd)));
	}
/* else
	nwd = 0; */

if( isicon(np, &k) && k==1)
	return((struct iogroup *)p);

dop = ALLOC(doblock);
dop->tag = TDOBLOCK;

dop->dovar = v = gent(TYINT, PNULL);
dop->dopar[0] = mkint(1);
dop->dopar[1] = simple(SUBVAL, np);
dop->dopar[2] = NULL;

q = simple(LVAL, q);
if(q->vsubs == NULL)
	q->vsubs = mknode(TLIST,0, mkchain(cpexpr(v),CHNULL), PNULL);
else
	((chainp)((struct exprblock *)q->vsubs)->leftp)->datap = simple(SUBVAL, mknode(TAROP,OPPLUS, cpexpr(v),
		     mknode(TAROP,OPMINUS,((chainp)((struct exprblock *)q->vsubs)->leftp)->datap,mkint(1))));
q->vdim = NULL;
gp = mkiogroup( mkchain(mkioitem(q,CNULL), CHNULL), p->iofmt, dop);
gp->nrep = nrep;
cfree(p);
return(gp);
}

ptr mkformat(letter, n1, n2)
char letter;
register ptr n1, n2;
{
char f[20], *fp, *s;
int k;

if(letter == 's')
	{
	if(n1)
		{
		k = conval(n1);
		frexpr(n1);
		}
	else	k = 1;

	for(fp = f; k-->0 ; )
		*fp++ = '/';
	*fp = '\0';
	return( (int *)copys(f) );
	}

f[0] = letter;
fp = f+1;

if(n1)	{
	n1 = simple(RVAL,n1);
	if(n1->tag==TCONST && n1->vtype==TYINT)
		{
		for(s = (char *)n1->leftp ; *s; )
			*fp++ = *s++;
		}
	else	execerr("bad format component %s", n1->leftp);
	frexpr(n1);
	}

if(n2)	{
	if(n2->tag==TCONST && n2->vtype==TYINT)
		{
		*fp++ = '.';
		for(s = (char *)n2->leftp ; *s; )
			*fp++ = *s++;
		}
	else	execerr("bad format component %s", n2->leftp);
	frexpr(n2);
	}

if( letter == 'x' )
	{
	if(n1 == 0)
		*fp++ = '1';
	fp[0] = 'x';
	fp[1] = '\0';
	return( (int *)copys(f+1) );
	}
else	{
	*fp = '\0';
	return( (int *)copys(f) );
	}
}

ptr mkioitem(e,f)
register ptr e;
char *f;
{
register ptr p;
char fmt[10];
ptr gentemp();

p = (int *)ALLOC(ioitem);
p->tag = TIOITEM;
if(e!=NULL && e->tag==TCONST)
	if(e->vtype==TYCHAR && (f==0 || (f[0]=='c' && f[1]=='\0') ))
		{
		p->ioexpr = 0;
		sprintf(msg, "%dh%s", strlen(e->leftp), e->leftp);
		p->iofmt = copys(msg);
		frexpr(e);
		return(p);
		}
	else	e = mknode(TASGNOP,OPASGN,gentemp(e),e);

if(e && e->vtype==TYCHAR && f && f[0]=='c' && f[1]=='\0')
	f = NULL;
if(f == NULL)
	{
	switch(e->vtype)
		{
		case TYINT:
		case TYREAL:
		case TYLREAL:
		case TYCOMPLEX:
		case TYLOG:
			f = copys( tailor.dfltfmt[e->vtype] );
			break;

		case TYCHAR:
			if(((struct headbits *)e->vtypep)->tag != TCONST)
				{
				execerr("no adjustable character formats", "");
				f = 0;
				}
			else	{
				sprintf(fmt, "c%s", ((struct exprblock *)e->vtypep)->leftp);
				f = copys(fmt);
				}
			break;

		default:
			execerr("cannot do I/O on structures", "");
			f = 0;
			break;
		}
	}

p->ioexpr = e;
p->iofmt = f;
return(p);
}



ptr arrsize(p)
ptr p;
{
register ptr b;
ptr f, q;

q = mkint(1);

if(b = p->vdim)
    for(b = b->datap ; b ; b = b->nextp)
	{
	if(b->upperb == 0) continue;
	f = cpexpr(b->upperb);
	if(b->lowerb)
		f = mknode(TAROP,OPPLUS,f,
			mknode(TAROP,OPMINUS,mkint(1),cpexpr(b->lowerb)));
	q = simple(RVAL, mknode(TAROP,OPSTAR,q,f));
	}
return(q);
}




repfac(dop)
register struct doblock *dop;
{
int m1, m2, m3;

m3 = 1;
if( isicon(dop->dopar[0],&m1) &&  isicon(dop->dopar[1],&m2) &&
  (dop->dopar[2]==NULL || isicon(dop->dopar[2],&m3)) )
	{
	if(m3 > 0)
		return(1 + (m2-m1)/m3);
	}
else	execerr("nonconstant implied do", "");
return(1);
}



ioop(s)
char *s;
{
if( equals(s, "backspace") )
	return(FBACKSPACE);
if( equals(s, "rewind") )
	return(FREWIND);
if( equals(s, "endfile") )
	return(FENDFILE);
return(0);
}




ptr exioop(p, errcheck)
register struct exprblock *p;
int errcheck;
{
register ptr q, t;

if( (q = p->rightp)==NULL || (q = q->leftp)==NULL  )
	{
	execerr("bad I/O operation", "");
	return(NULL);
	}
q = simple(LVAL, cpexpr(q->datap) );

exlab(0);
putic(ICKEYWORD, ioop(((struct stentry *)((struct typeblock *)p->leftp)->sthead)->namep));

if(errcheck)
	{
	if(tailor.errmode != IOERRFORT77)
		{
		execerr("cannot test value of IOOP without ftn77", "");
		return( errnode() );
		}
	putic(ICOP, OPLPAR);
	prexpr(q);
	putic(ICOP, OPCOMMA);
	putsii(ICCONST, "iostat =");
	prexpr(cpexpr( t = gent(TYINT,PNULL)));
	putic(ICOP, OPRPAR);
	return( t );
	}
else	{
	putic(ICBLANK, 1);
	prexpr(q);
	}
return 0;
}
