/*********************************************************************
 *   Copyright 1993, UCAR/Unidata
 *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
 *   $Header: /upc/share/CVS/netcdf-3/ncgen/genf77.c,v 1.4 2010/05/17 23:26:44 dmh Exp $
 *********************************************************************/

#include "includes.h"
#include "nciter.h"

#ifdef ENABLE_F77

#undef TRACE

/*MNEMONIC*/
#define USEMEMORY 1

static List* f77procs = NULL; /* bodies of generated procedures */

/* Forward */
static void genf77_definevardata(Symbol* vsym);
static void genf77_defineattr(Symbol* asym);
static void genf77_definevardata(Symbol*);

static void f77attrify(Symbol* asym, Bytebuffer* buf);
static const char* f77varncid(Symbol* vsym);
static const char* f77dimncid(Symbol* vsym);

static const char* nfstype(nc_type nctype);
static const char* nftype(nc_type type);
static const char* nfstype(nc_type nctype);
static const char* ncftype(nc_type type);
static const char* nfdtype(nc_type type);

static void f77skip(void);
static void f77comment(char* cmt);
static void f77fold(Bytebuffer* lines);
static void f77flush(void);

static void genf77_write(Generator*,Symbol*,Bytebuffer*,int,size_t*,size_t*);
static void genf77_writevar(Generator*,Symbol*,Bytebuffer*,int,size_t*,size_t*);
static void genf77_writeattr(Generator*,Symbol*,Bytebuffer*,int,size_t*,size_t*);


#ifdef USE_NETCDF4
static char* f77prefixed(List* prefix, char* suffix, char* separator);
#endif

/*
 * Generate code for creating netCDF from in-memory structure.
 */
void
gen_ncf77(const char *filename)
{
    int idim, ivar, iatt;
    int ndims, nvars, natts, ngatts;
    char* cmode_string;

    ndims = listlength(dimdefs);
    nvars = listlength(vardefs);
    natts = listlength(attdefs);
    ngatts = listlength(gattdefs);

    /* Construct the main program */

    f77skip();
    bbprintf0(stmt,"program %s\n", mainname, filename);
    codedump(stmt);
    bbprintf0(stmt,"* input file %s", filename);
    codeline("include 'netcdf.inc'");
    f77comment("error status return");
    codeline("integer stat");
    f77comment("netCDF ncid");
    codeline("integer  ncid");

    /* create necessary declarations */

    if (ndims > 0) {
	f77skip();
	f77comment("dimension lengths");
	for(idim = 0; idim < ndims; idim++) {
	    Symbol* dsym = (Symbol*)listget(dimdefs,idim);
	    bbprintf0(stmt,"integer %s_len\n",f77name(dsym));
	    codedump(stmt);
	    if(dsym->dim.declsize == NC_UNLIMITED) {
	        bbprintf0(stmt,"parameter (%s_len = NF_UNLIMITED)\n",
			f77name(dsym));
	    } else {
		bbprintf0(stmt,"parameter (%s_len = %lu)\n",
			f77name(dsym),
			(unsigned long) dsym->dim.declsize);
	    }
	    codedump(stmt);
	}
    }
    f77flush();

    /* Now create the dimension id's */
    if (ndims > 0) {
	f77skip();
	f77comment("dimension ids");
	for(idim = 0; idim < ndims; idim++) {
	    Symbol* dsym = (Symbol*)listget(dimdefs,idim);
	    bbprintf0(stmt,"integer %s_dim\n",f77name(dsym));
	    codedump(stmt);
	}
    }

    if (nvars > 0) {
	f77skip();
	f77comment("variable ids");
	for(ivar = 0; ivar < nvars; ivar++) {
            Symbol* vsym = (Symbol*)listget(vardefs,ivar);
	    bbprintf0(stmt,"integer %s;\n", f77varncid(vsym));
	    codedump(stmt);
	}

	f77skip();
	f77comment("rank (number of dimensions) for each variable");
	for(ivar = 0; ivar < nvars; ivar++) {
            Symbol* vsym = (Symbol*)listget(vardefs,ivar);
	    bbprintf0(stmt,"integer %s_rank\n", f77name(vsym));
	    codedump(stmt);
	    bbprintf0(stmt,"parameter (%s_rank = %d)\n",
		    f77name(vsym),
		    vsym->typ.dimset.ndims);
	    codedump(stmt);
	}
        f77skip();
        f77comment("variable shapes");
	for(ivar = 0; ivar < nvars; ivar++) {
            Symbol* vsym = (Symbol*)listget(vardefs,ivar);
	    if(vsym->typ.dimset.ndims > 0) {
		bbprintf0(stmt,"integer %s_dims(%s_rank)\n",
			    f77name(vsym), f77name(vsym));
		codedump(stmt);
	    }
	}
	/* variable declarations (for scalar and fixed sized only) */
        f77skip();
        f77comment("variable declarations");
	for(ivar = 0; ivar < nvars; ivar++) {
            Symbol* vsym = (Symbol*)listget(vardefs,ivar);
	    nc_type typecode = vsym->typ.basetype->typ.typecode;
	    if(vsym->data == NULL) continue;
	    if(typecode == NC_CHAR) continue;
	    if(vsym->typ.dimset.ndims == 0) {/* scalar */
                bbprintf0(stmt,"%s %s\n",
			nfdtype(typecode),f77name(vsym));
                codedump(stmt);
	    } else if(vsym->typ.dimset.dimsyms[0]->dim.declsize != NC_UNLIMITED) {
		int i;
		Bytebuffer* dimstring = bbNew();
		Dimset* dimset = &vsym->typ.dimset;
                /* Compute the dimensions (in reverse order for fortran) */
                for(i=dimset->ndims-1;i>=0;i--) {
	            char tmp[32];
	            Symbol* dsym = dimset->dimsyms[i];
	            nprintf(tmp,sizeof(tmp)," %lu",
			(unsigned long)dsym->dim.declsize);
	            bbCat(dimstring,tmp);
		}
  	        commify(dimstring);
                bbprintf0(stmt,"%s %s(%s)\n",
			    nfdtype(typecode),
			    f77name(vsym),
			    bbContents(dimstring));
	        codedump(stmt);
                bbFree(dimstring);
	    }
	}
    }
    f77flush();

    /* F77 (as defined for ncgen3) requires per-type vectors for attributes */
    if(ngatts > 0 || natts > 0) {
	nc_type nctype;
	int pertypesizes[NC_DOUBLE+1];
	for(nctype=0;nctype<=NC_DOUBLE;nctype++) {pertypesizes[nctype] = 0;}
	if(ngatts > 0) {
    	    for(iatt = 0; iatt < ngatts; iatt++) {
	        Symbol* gasym = (Symbol*)listget(gattdefs,iatt);
		int count = gasym->data->length;
		int typecode = gasym->typ.basetype->typ.typecode;
	        if(count == 0) continue;
		if(pertypesizes[typecode] < count)
		    pertypesizes[typecode] = count; /* keep max */
	    }
	}
	if(natts > 0) {
    	    for(iatt = 0; iatt < natts; iatt++) {
	        Symbol* asym = (Symbol*)listget(attdefs,iatt);
		int count = asym->data->length;
		int typecode = asym->typ.basetype->typ.typecode;
	        if(count == 0) continue;
		if(pertypesizes[typecode] < count)
		    pertypesizes[typecode] = count; /* keep max */
	    }
	}
	/* Now, define the per-type vectors */
        f77skip();
        f77comment("attribute vectors");
	for(nctype=NC_BYTE;nctype <= NC_DOUBLE;nctype++) {
	    char* basetype = "integer";
            if(nctype == NC_FLOAT) basetype = "real";
            else if(nctype == NC_DOUBLE) basetype = "double precision";
	    if(pertypesizes[nctype] > 0) {
	        bbprintf0(stmt,"%s %sval(%d)\n",
			basetype, ncftype(nctype),
			pertypesizes[nctype]);
	        codedump(stmt);
	    }
	}
    }

    /* create netCDF file, uses NC_CLOBBER mode */
    f77skip();
    f77skip();
    f77comment("enter define mode");

    if (!cmode_modifier) {
	cmode_string = "nf_clobber";
    } else if (cmode_modifier & NC_64BIT_OFFSET) {
	cmode_string = "nf_clobber|nf_64bit_offset";
    } else {
        derror("unknown cmode modifier: %d",cmode_modifier);
	cmode_string = "nf_clobber";
    }
    bbprintf0(stmt,"stat = nf_create('%s', %s, ncid);\n",
		 filename,cmode_string);
    codedump(stmt);
    codeline("call check_err(stat)");
    f77flush();
    
    /* define dimensions from info in dims array */
    if (ndims > 0) {
	f77skip();
	f77comment("define dimensions");
        for(idim = 0; idim < ndims; idim++) {
            Symbol* dsym = (Symbol*)listget(dimdefs,idim);
    	    bbprintf0(stmt,
		"stat = nf_def_dim(ncid, %s, %s_len, %s);\n",
                f77escapifyname(dsym->name), f77name(dsym), f77dimncid(dsym));
	    codedump(stmt);
	    codeline("call check_err(stat)");
       }
    }
    f77flush();

    /* define variables from info in vars array */
    if (nvars > 0) {
	f77skip();
	f77comment("define variables");
	for(ivar = 0; ivar < nvars; ivar++) {
            Symbol* vsym = (Symbol*)listget(vardefs,ivar);
            Symbol* basetype = vsym->typ.basetype;
	    Dimset* dimset = &vsym->typ.dimset;
	    f77skip();
	    if(dimset->ndims > 0) {
		/* Remember; FORTRAN dimension order is reversed */
	        for(idim = 0; idim < dimset->ndims; idim++) {
		    int reverse = (dimset->ndims - idim) - 1;
		    Symbol* dsym = dimset->dimsyms[reverse];
		    bbprintf0(stmt,
			    "%s_dims(%d) = %s\n",
			    f77name(vsym),
			    idim+1,
			    f77dimncid(dsym));
		    codedump(stmt);
		}
	    }
	    bbprintf0(stmt,
			"stat = nf_def_var(ncid, %s, %s, %s_rank, %s, %s);\n",
			f77escapifyname(vsym->name),
			nftype(basetype->typ.typecode),
			f77name(vsym),
			(dimset->ndims == 0?"0":poolcat(f77name(vsym),"_dims")),
			f77varncid(vsym));
	    codedump(stmt);
	    codeline("call check_err(stat)");
	}
    }
    f77flush();
    
    /* Define the global attributes*/
    if(ngatts > 0) {
	f77skip();
	f77comment("assign global attributes");
	for(iatt = 0; iatt < ngatts; iatt++) {
	    Symbol* gasym = (Symbol*)listget(gattdefs,iatt);
	    genf77_defineattr(gasym);	    
	}
	f77skip();
    }
    f77flush();
    
    /* Define the variable specific attributes*/
    if(natts > 0) {
	f77skip();
	f77comment("assign per-variable attributes");
	for(iatt = 0; iatt < natts; iatt++) {
	    Symbol* asym = (Symbol*)listget(attdefs,iatt);
	    genf77_defineattr(asym);
	}
	f77skip();
    }
    f77flush();

    if (nofill_flag) {
        f77comment("don't initialize variables with fill values");
	codeline("stat = nf_set_fill(ncid, NC_NOFILL, 0);");
	codeline("call check_err(stat)");
    }

    f77skip();
    f77comment("leave define mode");
    codeline("stat = nf_enddef(ncid);");
    codeline("call check_err(stat)");
    f77skip();
    f77flush();

    if(!header_only) {
        /* Assign scalar variable data and non-unlimited arrays in-line */
        if(nvars > 0) {
            f77skip();
            f77skip();
            f77comment("assign scalar and fixed dimension variable data");
            for(ivar = 0; ivar < nvars; ivar++) {
                Symbol* vsym = (Symbol*)listget(vardefs,ivar);
                if(vsym->data == NULL) continue;
                if(vsym->typ.dimset.ndims == 0)
                    genf77_definevardata(vsym);
            }
            f77skip();
        }
    
        /* Invoke write procedures */
        if(nvars > 0) {
            List* calllist;
            f77skip();
            f77skip();
            f77comment("perform variable data writes");
            for(ivar = 0; ivar < nvars; ivar++) {
                int i;
                Symbol* vsym = (Symbol*)listget(vardefs,ivar);
                /* Call the procedures for writing unlimited variables */
                if(vsym->data != NULL
                    && vsym->typ.dimset.ndims > 0) {
                    genf77_definevardata(vsym);
                }
                /* dump any calls */
                generator_getstate(f77_generator,(void*)&calllist);
                ASSERT(calllist != NULL);
                for(i=0;i<listlength(calllist);i++) {
                    char* callstmt = (char*)listget(calllist,i);
                    codeline(callstmt);
                }       
                listclear(calllist);
            }
        }
    
        /* Close the file */
        codeline("stat = nf_close(ncid)");
        codeline("call check_err(stat)");
        codeline("end");

        /* Generate the write procedures */
        if(listlength(f77procs) > 0) {
	    int i;
    	    f77skip();
            for(i=0;i<listlength(f77procs);i++) {
    	        Bytebuffer* proctext = (Bytebuffer*)listget(f77procs,i);
    	        codedump(proctext);
    	        bbFree(proctext);
    	    }
    	    listfree(f77procs); f77procs = NULL;
	    f77skip();
        }
    }
    f77flush();

    /* Generate the check_err procedure */
    f77skip();
    codeline("subroutine check_err(stat)");
    codeline("integer stat");
    codeline("include 'netcdf.inc'");
    codeline("if (stat .ne. NF_NOERR) then");
    codeline("print *, nf_strerror(stat)");
    codeline("stop");
    codeline("endif");
    codeline("end");
    f77flush();

}

void
cl_f77(void)
{
   /* already done above */
}

/* Compute the name for a given var's id*/
/* Watch out: the result is a static*/
static const char*
f77varncid(Symbol* vsym)
{
    const char* tmp1;
    char* vartmp;
    tmp1 = f77name(vsym);
    vartmp = poolalloc(strlen(tmp1)+strlen("_id")+1);
    strcpy(vartmp,tmp1);
    strcat(vartmp,"_id");
    return vartmp;
}

/* Compute the name for a given dim's id*/
/* Watch out: the result is a static*/
static const char*
f77dimncid(Symbol* dsym)
{
    const char* tmp1;
    char* dimtmp;
    tmp1 = f77name(dsym);
    dimtmp = poolalloc(strlen(tmp1)+strlen("_dim")+1);
    strcpy(dimtmp,tmp1);
    strcat(dimtmp,"_dim");
    return dimtmp;
}

/* Compute the name for a given type*/
const char*
f77typename(Symbol* tsym)
{
    const char* name;
    ASSERT(tsym->objectclass == NC_TYPE);
    if(tsym->subclass == NC_PRIM)
	name = nftype(tsym->typ.typecode);
    else
        name = f77name(tsym);
    return name;
}

/* Compute the name for a given symbol*/
/* Cache in symbol->lname*/
const char*
f77name(Symbol* sym)
{
    if(sym->lname == NULL) {
	char* name = pooldup(sym->name);
#ifdef USE_NETCDF4
	if(sym->subclass == NC_FIELD || sym->subclass == NC_ECONST) {
	     sym->lname = nulldup(decodify(name));
	} else
#endif
	if(sym->objectclass == NC_ATT && sym->att.var != NULL) {
	    /* Attribute name must be prefixed with the f77name of the*/
	    /* associated variable*/
	    const char* vname = f77name(sym->att.var);
	    const char* aname = decodify(name);
	    sym->lname = (char*)emalloc(strlen(vname)
					+strlen(aname)
					+1+1);
	    sym->lname[0] = '\0';
            strcpy(sym->lname,vname);
	    strcat(sym->lname,"_");
	    strcat(sym->lname,aname);
	} else {
            /* convert to language form*/
#ifdef USE_NETCDF4
            sym->lname = nulldup(decodify(f77prefixed(sym->prefix,name,"_")));
#else
            sym->lname = nulldup(decodify(name)); /* convert to usable form*/
#endif
	}
    }
    return sym->lname;
}

static void
genf77_defineattr(Symbol* asym)
{
    Bytebuffer* code = bbNew();
    List* oldstate = NULL;
    generator_getstate(f77_generator,(void*)&oldstate);
    listfree(oldstate);
    generator_reset(f77_generator,(void*)listnew());
    generate_attrdata(asym,f77_generator,(Writer)genf77_write,code);
    bbFree(code);
}

static void
f77skip(void)
{
    codeline("");
}

static void
f77comment(char* cmt)
{
    codepartial("* ");
    codeline(cmt);
}

static void
f77fold(Bytebuffer* lines)
{
    char* s;
    char* line0;
    char* linen;
    static char trimchars[] = " \t\r\n";

    s = bbDup(lines);
    bbClear(lines);
    line0 = s;
    /* Start by trimming leading blanks and empty lines */
    while(*line0 && strchr(trimchars,*line0) != NULL) line0++;
    if(*line0 == '\0') return;
    for(;;) {
	size_t linelen;
	linen = line0;
	/* collect a single line */
	while(*linen != '\n' && *linen != '\0') linen++;
	if(*linen == '\0') break;
	linen++; /* include trailing newline */
	linelen = (linen - line0);
	/* handle comments and empty lines */
	if(*line0 == '*' || linelen == 1) {
	    bbAppendn(lines,line0,linelen);
	    line0 = linen;
	    continue;
	}
	/* Not a comment */
        /* check to see if we need to fold it (watch out for newline)*/
	if(linelen <= (F77_MAX_STMT+1)) { /* no folding needed */
	    bbCat(lines,"      "); /*indent*/
	    bbAppendn(lines,line0,linelen);
	    line0 = linen;
	    continue;
	}	 
	/* We need to fold */
        bbCat(lines,"      "); /*indent first line */
	while(linelen > F77_MAX_STMT) {
	    int incr = F77_MAX_STMT;
	    /* Check to ensure we are folding at a legal point */
	    if(*(line0+(incr-1)) == '\\') incr--;
	    bbAppendn(lines,line0,incr);
	    bbCat(lines,"\n     1"); /* comment extender */
	    line0 += incr;
	    linelen -= incr;
	}
	/* Do last part of the line */
	bbAppendn(lines,line0,linelen);
	line0 = linen;
    }
}

static void
f77flush(void)
{
    if(bbLength(codebuffer) > 0) {
        bbNull(codebuffer);
	f77fold(codebuffer);
        codeflush();
    }
}

static char* f77attrifyr(Symbol*, char* p, Bytebuffer* buf);

static void
f77attrify(Symbol* asym, Bytebuffer* buf)
{
    char* list,*p;

    if(bbLength(buf) == 0) return;
    list = bbDup(buf);
    p = list;
    bbClear(buf);
    f77attrifyr(asym,p,buf);
    bbNull(buf);
    efree(list);
}

static char*
f77attrifyr(Symbol* asym, char* p, Bytebuffer* buf)
{
    Symbol* basetype = asym->typ.basetype;
    nc_type typecode = basetype->typ.typecode;
    int c;
    int index;
    char where[1024];

    nprintf(where,sizeof(where),"%sval",ncftype(typecode));
    for(index=1;(c=*p);) {
	if(c == ' ' || c == ',') {p++; continue;}
	bbprintf0(stmt,"%s(%d) = ",where,index);
	bbCatbuf(buf,stmt);
        p=word(p,buf);
        bbCat(buf,"\n");
	index++;
    }    
    return p;
}

#ifdef USE_NETCDF4
/* Result is pool alloc'd*/
static char*
f77prefixed(List* prefix, char* suffix, char* separator)
{
    int slen;
    int plen;
    int i;
    char* result;

    ASSERT(suffix != NULL);
    plen = prefixlen(prefix);
    if(prefix == NULL || plen == 0) return decodify(suffix);
    /* plen > 0*/
    slen = 0;
    for(i=0;i<plen;i++) {
	Symbol* sym = (Symbol*)listget(prefix,i);
	slen += (strlen(sym->name)+strlen(separator));
    }
    slen += strlen(suffix);
    slen++; /* for null terminator*/
    result = poolalloc(slen);
    result[0] = '\0';
    /* Leave off the root*/
    i = (rootgroup == (Symbol*)listget(prefix,0))?1:0;
    for(;i<plen;i++) {
	Symbol* sym = (Symbol*)listget(prefix,i);
        strcat(result,sym->name); /* append "<prefix[i]/>"*/
	strcat(result,separator);
    }    
    strcat(result,suffix); /* append "<suffix>"*/
    return result;
}
#endif

/* return FORTRAN name for netCDF type, given type code */
static const char*
nftype(nc_type type)
{
    switch (type) {
      case NC_CHAR: return "nf_char";
      case NC_BYTE: return "nf_byte";
      case NC_SHORT: return "nf_short";
      case NC_INT: return "nf_int";
      case NC_FLOAT: return "nf_float";
      case NC_DOUBLE: return "nf_double";
      default: PANIC("nctype: bad type code");
    }
    return NULL;
}

/* return FORTRAN declaration type for given type code */
static const char*
nfdtype(nc_type type)
{
    switch (type) {
      case NC_CHAR: return "integer";
      case NC_BYTE: return "integer";
      case NC_SHORT: return "integer";
      case NC_INT: return "integer";
      case NC_FLOAT: return "real ";
      case NC_DOUBLE: return "double precision";
      default: PANIC("nctype: bad type code");
    }
    return NULL;
}

/*
 * Return proper _put_var_ suffix for given nc_type
 */
static const char* 
nfstype(nc_type nctype)
{
    switch (nctype) {
      case NC_CHAR:
	return "text";
      case NC_BYTE:
	return "int";
      case NC_SHORT:
	return "int";
      case NC_INT:
	return "int";
      case NC_FLOAT:
	return "real";
      case NC_DOUBLE:
	return "double";
      default:
	derror("ncstype: bad type code: %d",nctype);
	return 0;
    }
}

/*
 * Return FORTRAN type name for netCDF attribute type
 */
static const char* 
ncftype(nc_type type)
{
    switch (type) {
      case NC_CHAR:
	return "text";
      case NC_BYTE:
	return "int1";
      case NC_SHORT:
	return "int2";
      case NC_INT:
	return "int";
      case NC_FLOAT:
	return "real";
      case NC_DOUBLE:
	return "double";
      default:
	PANIC1("ncctype: bad type code:%d",type);
    }
    return 0;
}

static void
genf77_definevardata(Symbol* vsym)
{
    Bytebuffer* code = bbNew();
    List* oldstate = NULL;
    generator_getstate(f77_generator,(void*)&oldstate);
    listfree(oldstate);
    generator_reset(f77_generator,(void*)listnew());
    generate_vardata(vsym,f77_generator,(Writer)genf77_write,code);
    bbFree(code);
}

static void
genf77_write(Generator* generator, Symbol* sym, Bytebuffer* code,
             int rank, size_t* start, size_t* count)
{
    if(sym->objectclass == NC_ATT)
	genf77_writeattr(generator,sym,code,rank,start,count);
    else if(sym->objectclass == NC_VAR) {
	genf77_writevar(generator,sym,code,rank,start,count);
    }
    else
	PANIC("illegal symbol for genf77_write");
}

static void
genf77_writevar(Generator* generator, Symbol* vsym, Bytebuffer* code,
           int rank, size_t* start, size_t* count)
{
    Dimset* dimset = &vsym->typ.dimset;
    int typecode = vsym->typ.basetype->typ.typecode;
    int i;

    /* Deal with character variables specially */
    if(typecode == NC_CHAR) {
        f77quotestring(code);
        bbprintf0(stmt,"stat = nf_put_var_%s(ncid, %s, %s)\n",
	        nfstype(typecode),
		f77varncid(vsym),
		bbContents(code));
        codedump(stmt);
        codeline("call check_err(stat)");
	f77skip();
    } else if(rank == 0) {
	commify(code); /* insert commas as needed */
        bbprintf0(stmt,"data %s /%s/\n",
			    f77name(vsym),bbContents(code));
	codedump(stmt);
        bbprintf0(stmt,"stat = nf_put_var_%s(ncid, %s, %s)\n",
	        nfstype(typecode),
		f77varncid(vsym),
		f77name(vsym));
        codedump(stmt);
        codeline("call check_err(stat)");
	f77skip();
    } else { /* rank > 0 && typecode != NC_CHAR*/
        char* dimstring;
	int index = listlength(f77procs);
	Bytebuffer* proctext;
	Bytebuffer* save;
	List* calllist;

	/* Generate the call to the procedure */
        bbprintf0(stmt,"call write_%s_%d(ncid,%s_id_%d)\n",
	        		f77name(vsym),index,f77name(vsym));
	/* save in the generator state */
	generator_getstate(generator,(void*)&calllist);
	ASSERT(calllist != NULL);
	listpush(calllist,(elem_t)bbDup(stmt));

        /* Construct the procedure body and save it */
	proctext = bbNew();
	save = codebuffer;
	codebuffer = proctext;
	f77skip();
        bbprintf0(stmt,"subroutine write_%s_%d(ncid,%s_id)\n",
                        f77name(vsym),index,f77name(vsym));
        codedump(stmt);
        codeline("integer ncid");
        bbprintf0(stmt,"integer %s_id\n",f77name(vsym));
        codedump(stmt);
        codeline("include 'netcdf.inc'");
        codeline("integer stat");
        f77skip();
        bbprintf0(stmt,"integer %s_start(%u)\n",
                        f77name(vsym),(unsigned int)rank);
        codedump(stmt);
        bbprintf0(stmt,"integer %s_count(%u)\n",
                        f77name(vsym),(unsigned int)rank);
        codedump(stmt);
        f77skip();

        /* Compute the dimensions (in reverse order for fortran) */
	bbClear(stmt);
        for(i=rank-1;i>=0;i--) {
            char tmp[32];
            nprintf(tmp,sizeof(tmp),"%s%lu",
			(i==(rank-1)?"":","),
			count[i]);
            bbCat(stmt,tmp);
        }
        dimstring = bbDup(stmt);
        commify(code);
        bbprintf0(stmt,"%s %s(%s)\n",
                                nfdtype(typecode),
                                f77name(vsym),
                                dimstring);
        efree(dimstring);
        codedump(stmt);

        /* Generate the data // statement */
	commify(code); /* insert commas as needed */
        bbprintf0(stmt,"data %s /",f77name(vsym));
        bbCatbuf(stmt,code);
        bbCat(stmt,"/\n");
        codedump(stmt);

	/* Set the values for the start and count sets
	   but in reverse order
	*/
	for(i=0;i<dimset->ndims;i++) {
	    int reverse = (dimset->ndims - i) - 1;
	    bbprintf0(stmt,"%s_start(%d) = %lu\n",
		    f77name(vsym),
		    i+1,
		    start[reverse]+1); /* +1 for FORTRAN */
	    codedump(stmt);
	}
	for(i=0;i<dimset->ndims;i++) {
	    int reverse = (dimset->ndims - i) - 1;
	    bbprintf0(stmt,"%s_count(%d) = %lu\n",
		f77name(vsym),
		i+1,
		count[reverse]);
	    codedump(stmt);
	}
	bbprintf0(stmt,"stat = nf_put_vara_%s(ncid, %s, %s_start, %s_count, ",
		nfstype(typecode),
		f77varncid(vsym),
		f77name(vsym),
		f77name(vsym));
	codedump(stmt);
	if(typecode == NC_CHAR) {
	    f77quotestring(code);
	    codedump(code);
	} else {
	    codeprintf("%s",f77name(vsym));
	}
	codeline(")");
	codeline("call check_err(stat)");
	/* Close off the procedure */
	codeline("end");
        /* save the generated procedure(s) */
	if(f77procs == NULL) f77procs = listnew();
        listpush(f77procs,(elem_t)codebuffer);
        codebuffer = save;
    }
}

static void
genf77_writeattr(Generator* generator, Symbol* asym, Bytebuffer* code,
	       int rank, size_t* start, size_t* count)
{
    Datalist* list;
    Symbol* basetype = asym->typ.basetype;
    size_t len = asym->data->length; /* default assumption */

    list = asym->data;
    len = list==NULL?0:list->length;

    bbprintf0(stmt,"* define %s\n",asym->name);
    codedump(stmt);

    /* Use the specialized put_att_XX routines if possible*/
    switch (basetype->typ.typecode) {
    case NC_BYTE:
    case NC_SHORT:
    case NC_INT:
    case NC_FLOAT:
    case NC_DOUBLE:
	f77attrify(asym,code);
	codedump(code);
	bbClear(code);
	bbprintf0(stmt,"stat = nf_put_att_%s(ncid, %s, %s, %s, %lu, %sval)\n",
		nfstype(basetype->typ.typecode),
		(asym->att.var == NULL?"NF_GLOBAL"
				      :f77varncid(asym->att.var)),
		f77escapifyname(asym->name),
		nftype(basetype->typ.typecode),		
		len,
		ncftype(basetype->typ.typecode));
	codedump(stmt);
	break;

    case NC_CHAR:
	len = bbLength(code);
	f77quotestring(code);	
	if(len==0) len++;
	bbprintf0(stmt,"stat = nf_put_att_text(ncid, %s, %s, %lu, ",
		(asym->att.var == NULL?"NF_GLOBAL"
				      :f77varncid(asym->att.var)),
		f77escapifyname(asym->name),
		len);
	codedump(stmt);
	codedump(code);
	codeline(")");
	break;


    default: /* User defined type */
	verror("Non-classic type: %s",nctypename(basetype->typ.typecode));
	break;
    }

    codeline("call check_err(stat)");
}

#endif /*ENABLE_F77*/