/*********************************************************************
 *   Copyright 1993, UCAR/Unidata
 *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
 *   $Header: /upc/share/CVS/netcdf-3/ncdump/vardata.c,v 1.13 2006/03/04 18:50:15 ed Exp $
 *********************************************************************/

#include <config.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#ifndef NO_FLOAT_H
#include <float.h>		/* for FLT_EPSILON, DBL_EPSILON */
#endif /* NO_FLOAT_H */

#include <netcdf.h>
#include "ncdump.h"
#include "dumplib.h"
#include "vardata.h"

static float float_epsilon(void);
static double double_epsilon(void);
static void init_epsilons(void);
static void printbval(char* sout, const char* fmt, const ncvar_t* varp,
		      signed char val);
static void printsval(char* sout, const char* fmt, const ncvar_t* varp,
		      short val);
static void printival(char* sout, const char* fmt, const ncvar_t* varp,
		      int val);
static void printfval(char* sout, const char* fmt, const ncvar_t* varp,
		      float val);
static void printdval(char* sout, const char* fmt, const ncvar_t* varp,
		      double val);
static void print_any_val(char *sout, const char *fmt, const ncvar_t *varp, 
			  const nc_type type, void *val);
static void lastdelim(boolean  more, boolean lastrow);
static void annotate(const ncvar_t* vp, const fspec_t* fsp,
		     const size_t* cor, long iel);
static void pr_tvals(const ncvar_t *vp, size_t len, const char *fmt,
		     boolean more, boolean lastrow, const char *vals,
		     const fspec_t* fsp, const size_t *cor);
static void pr_bvals(const ncvar_t *vp, size_t len, const char *fmt,
		     boolean more, boolean lastrow, const signed char *vals,
		     const fspec_t* fsp, const size_t *cor);
static void pr_svals(const ncvar_t *vp, size_t len, const char *fmt,
		     boolean more, boolean lastrow, const short *vals,
		     const fspec_t* fsp, const size_t *cor);
static void pr_ivals(const ncvar_t *vp, size_t len, const char *fmt,
		     boolean more, boolean lastrow, const int *vals,
		     const fspec_t* fsp, const size_t *cor);
static void pr_fvals(const ncvar_t *vp, size_t len, const char *fmt,
		     boolean more, boolean lastrow, const float *vals,
		     const fspec_t* fsp, const size_t *cor);
static void pr_dvals(const ncvar_t *vp, size_t len, const char *fmt,
		     boolean more, boolean lastrow, const double *vals,
		     const fspec_t* fsp, const size_t *cor);
static int  upcorner(const size_t* dims, int ndims, size_t* odom,
		     const size_t* add);
static void lastdelim2 (boolean more, boolean lastrow);

#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)

static float float_eps;
static double double_eps;

static float
float_epsilon(void)
{
    float float_eps;
#ifndef NO_FLOAT_H
    float_eps = FLT_EPSILON;
#else /* NO_FLOAT_H */
    {
	float etop, ebot, eps;
	float one = 1.0;
	float two = 2.0;
	etop = 1.0;
	ebot = 0.0;
	eps = ebot + (etop - ebot)/two;
	while (eps != ebot && eps != etop) {
	    float epsp1;

	    epsp1 = one + eps;
	    if (epsp1 > one)
		etop = eps;
	    else
		ebot = eps;
	    eps = ebot + (etop - ebot)/two;
	}
	float_eps = two * etop;
    }
#endif /* NO_FLOAT_H */
    return float_eps;
}


static double
double_epsilon(void)
{
    double double_eps;
#ifndef NO_FLOAT_H
    double_eps = DBL_EPSILON;
#else /* NO_FLOAT_H */
    {
	double etop, ebot, eps;
	double one = 1.0;
	double two = 2.0;
	etop = 1.0;
	ebot = 0.0;
	eps = ebot + (etop - ebot)/two;
	while (eps != ebot && eps != etop) {
	    double epsp1;

	    epsp1 = one + eps;
	    if (epsp1 > one)
		etop = eps;
	    else
		ebot = eps;
	    eps = ebot + (etop - ebot)/two;
	}
	double_eps = two * etop;
    }
#endif /* NO_FLOAT_H */
    return double_eps;
}


static void
init_epsilons(void)
{
    float_eps = float_epsilon();
    double_eps = double_epsilon();
}

/*
 * Output a value of a byte variable, except if there is a fill value for
 * the variable and the value is the fill value, print the fill-value string
 * instead.
 */
static void
printbval(
    char *sout,			/* string where output goes */
    const char *fmt,		/* printf format used for value */
    const ncvar_t *varp,	/* variable */
    signed char val		/* value */
    )
{
    if (varp->has_fillval) {
	double fillval = varp->fillval;
	if(fillval == val) {
	    (void) sprintf(sout, FILL_STRING);
	    return;
	}
    }
    (void) sprintf(sout, fmt, val);
}

/*
 * Output a value of a short variable, except if there is a fill value for
 * the variable and the value is the fill value, print the fill-value string
 * instead.
 */
static void
printsval(
    char *sout,			/* string where output goes */
    const char *fmt,		/* printf format used for value */
    const ncvar_t *varp,		/* variable */
    short val			/* value */
    )
{
    if (varp->has_fillval) {
	double fillval = varp->fillval;
	if(fillval == val) {
	    (void) sprintf(sout, FILL_STRING);
	    return;
	}
    }
    (void) sprintf(sout, fmt, val);
}


/*
 * Output a value of an int variable, except if there is a fill value for
 * the variable and the value is the fill value, print the fill-value string
 * instead.
 */
static void
printival(
    char *sout,			/* string where output goes */
    const char *fmt,		/* printf format used for value */
    const ncvar_t *varp,		/* variable */
    int val			/* value */
    )
{
    if (varp->has_fillval) {
	int fillval = (int)varp->fillval;
	if(fillval == val) {
	    (void) sprintf(sout, FILL_STRING);
	    return;
	}
    }
    (void) sprintf(sout, fmt, val);
}


#define absval(x)  ( (x) < 0 ? -(x) : (x) )

/*
 * Output a value of a float variable, except if there is a fill value for
 * the variable and the value is the fill value, print the fill-value string
 * instead.  Floating-point fill values need only be within machine epsilon of
 * defined fill value.
 */
static void
printfval(
    char *sout,			/* string where output goes */
    const char *fmt,		/* printf format used for value */
    const ncvar_t *varp,		/* variable */
    float val			/* value */
    )
{
    if(varp->has_fillval) {
	double fillval = varp->fillval;
	if((val > 0) == (fillval > 0) && /* prevents potential overflow */
	   (absval(val - fillval) <= absval(float_eps * fillval))) {
	    (void) sprintf(sout, FILL_STRING);
	    return;
	}
    }
    (void) sprintf(sout, fmt, val);
}


/*
 * Output a value of a double variable, except if there is a fill value for
 * the variable and the value is the fill value, print the fill-value string
 * instead.  Floating-point fill values need only be within machine epsilon of
 * defined fill value.
 */
static void
printdval(
    char *sout,			/* string where output goes */
    const char *fmt,		/* printf format used for value */
    const ncvar_t *varp,		/* variable */
    double val			/* value */
    )
{
    if(varp->has_fillval) {
	double fillval = varp->fillval;
	if((val > 0) == (fillval > 0) && /* prevents potential overflow */
	   (absval(val - fillval) <= absval(double_eps * fillval))) {
	    (void) sprintf(sout, FILL_STRING);
	    return;
	}
    }
    (void) sprintf(sout, fmt, val);
}

#ifdef USE_NETCDF4
/*
 * Output a value of a double variable, except if there is a fill value for
 * the variable and the value is the fill value, print the fill-value string
 * instead.  Floating-point fill values need only be within machine epsilon of
 * defined fill value.
 */
static void
print_any_val(char *sout, const char *fmt, const ncvar_t *varp, 
	      const nc_type type, void *val)
{
    switch(type)
    {
       case NC_INT64:
	  sprintf(sout, fmt, *(long long *)val);
	  (long long *)val++;
	  break;
       default:
	  break;
    }
/*     if(varp->has_fillval) { */
/* 	double fillval = varp->fillval; */
/* 	if((val > 0) == (fillval > 0) && /\* prevents potential overflow *\/ */
/* 	   (absval(val - fillval) <= absval(double_eps * fillval))) { */
/* 	    (void) sprintf(sout, FILL_STRING); */
/* 	    return; */
/* 	} */
/*     } */
}
#endif /* USE_NETCDF4 */

/*
 * print last delimiter in each line before annotation (, or ;)
 */
static void
lastdelim (boolean more, boolean lastrow)
{
    if (more) {
	Printf(", ");
    } else {
	if(lastrow) {
	    Printf(";");
	} else {
	    Printf(",");
	}
    }
}

/*
 * print last delimiter in each line before annotation (, or ;)
 */
static void
lastdelim2 (boolean more, boolean lastrow)
{
    if (more) {
	lput(", ");
    } else {
	if(lastrow) {
	    lput(" ;");
	    lput("\n");
	} else {
	    lput(",\n");
	    lput("  ");
	}
    }
}


/*
 * Annotates a value in data section with var name and indices in comment
 */
static void
annotate(
     const ncvar_t *vp,	/* variable */
     const fspec_t* fsp,	/* formatting specs */
     const size_t *cor,		/* corner coordinates */
     long iel			/* which element in current row */
     )
{
    int vrank = vp->ndims;
    int id;
    
    /* print indices according to data_lang */
    (void) printf("  // %s(", vp->name);
    switch (fsp->data_lang) {
      case LANG_C:
	/* C variable indices */
	for (id = 0; id < vrank-1; id++)
	  Printf("%lu,", (unsigned long) cor[id]);
	Printf("%lu", (unsigned long) cor[id] + iel);
	break;
      case LANG_F:
	/* Fortran variable indices */
	Printf("%lu", (unsigned long) cor[vrank-1] + iel + 1);
	for (id = vrank-2; id >=0 ; id--) {
	    Printf(",%lu", 1 + (unsigned long) cor[id]);
	}
	break;
    }
    Printf(")\n    ");
}


/*
 * Print a number of char variable values, where the optional comments
 * for each value identify the variable, and each dimension index.
 */
static void
pr_tvals(
     const ncvar_t *vp,		/* variable */
     size_t len,		/* number of values to print */
     const char *fmt,		/* printf format used for each value.  If
				 * nc_type is NC_CHAR and this is NULL,
				 * character arrays will be printed as
				 * strings enclosed in quotes.  */
     boolean more,		/* true if more data for this row will
				 * follow, so add trailing comma */
     boolean lastrow,		/* true if this is the last row for this
				 * variable, so terminate with ";" instead
				 * of "," */
     const char *vals,		/* pointer to block of values */
     const fspec_t* fsp,          /* formatting specs */
     const size_t *cor		/* corner coordinates */
     )
{
    long iel;
    const char *sp;
    unsigned char uc;
    char sout[100];		/* temporary string for each encoded output */

    if (fmt == 0 || STREQ(fmt,"%s") || STREQ(fmt,"")) { /* as string */
	Printf("\"");
	/* adjust len so trailing nulls don't get printed */
	sp = vals + len;
	while (len != 0 && *--sp == '\0')
	    len--;
	for (iel = 0; iel < len; iel++)
	    switch (uc = *vals++ & 0377) {
	    case '\b':
		Printf("\\b");
		break;
	    case '\f':
		Printf("\\f");
		break;
	    case '\n':	/* generate linebreaks after new-lines */
		Printf("\\n\",\n    \"");
		break;
	    case '\r':
		Printf("\\r");
		break;
	    case '\t':
		Printf("\\t");
		break;
	    case '\v':
		Printf("\\v");
		break;
	    case '\\':
		Printf("\\\\");
		break;
	    case '\'':
		Printf("\\\'");
		break;
	    case '\"':
		Printf("\\\"");
		break;
	    default:
		if (isprint(uc))
		    Printf("%c",uc);
		else
		    Printf("\\%.3o",uc);
		break;
	    }
	Printf("\"");
	if (fsp->full_data_cmnts) {
	    lastdelim (more, lastrow);
	    annotate (vp, fsp,  (size_t *)cor, 0L);
	}
    } else {		/* use format from C_format attribute */
	for (iel = 0; iel < len-1; iel++) {
	    if (fsp->full_data_cmnts) {
		Printf(fmt, *vals++);
		Printf(", ");
		annotate (vp, fsp,  (size_t *)cor, iel);
	    } else {
		(void) sprintf(sout, fmt, *vals++);
		(void) strcat(sout, ", ");
		lput(sout);
	    }
	}
	if (fsp->full_data_cmnts) {
	    Printf(fmt, *vals++);
	    lastdelim (more, lastrow);
	    annotate (vp, fsp,  (size_t *)cor, iel);
	} else {
	    (void) sprintf(sout, fmt, *vals++);
	    lput(sout);
	}
    }
    if (!fsp->full_data_cmnts) {
	lastdelim2 (more, lastrow);
    }
}


/*
 * Print a number of byte variable values, where the optional comments
 * for each value identify the variable, and each dimension index.
 */
static void
pr_bvals(
     const ncvar_t *vp,	/* variable */
     size_t len,		/* number of values to print */
     const char *fmt,		/* printf format used for each value.  If
				 * nc_type is NC_CHAR and this is NULL,
				 * character arrays will be printed as
				 * strings enclosed in quotes.  */
     boolean more,		/* true if more data for this row will
				 * follow, so add trailing comma */
     boolean lastrow,		/* true if this is the last row for this
				 * variable, so terminate with ";" instead
				 * of "," */
     const signed char *vals,	/* pointer to block of values */
     const fspec_t* fsp,	        /* formatting specs */
     const size_t *cor		/* corner coordinates */
     )
{
    long iel;
    char sout[100];		/* temporary string for each encoded output */

    for (iel = 0; iel < len-1; iel++) {
	printbval(sout, fmt, vp, *vals++);
	if (fsp->full_data_cmnts) {
	    Printf(sout);
	    Printf(",");
	    annotate (vp, fsp, cor, iel);
	} else {
	    (void) strcat(sout, ", ");
	    lput(sout);
	}
    }
    printbval(sout, fmt, vp, *vals++);
    if (fsp->full_data_cmnts) {
	Printf(sout);
	lastdelim (more, lastrow);
	annotate (vp, fsp, cor, iel);
    } else {
	lput(sout);
	lastdelim2 (more, lastrow);
    }
}


/*
 * Print a number of short variable values, where the optional comments
 * for each value identify the variable, and each dimension index.
 */
static void
pr_svals(
     const ncvar_t *vp,		/* variable */
     size_t len,		/* number of values to print */
     const char *fmt,		/* printf format used for each value.  If
				 * nc_type is NC_CHAR and this is NULL,
				 * character arrays will be printed as
				 * strings enclosed in quotes.  */
     boolean more,		/* true if more data for this row will
				 * follow, so add trailing comma */
     boolean lastrow,		/* true if this is the last row for this
				 * variable, so terminate with ";" instead
				 * of "," */
     const short *vals,		/* pointer to block of values */
     const fspec_t* fsp,	        /* formatting specs */
     const size_t *cor		/* corner coordinates */
     )
{
    long iel;
    char sout[100];		/* temporary string for each encoded output */

    for (iel = 0; iel < len-1; iel++) {
	printsval(sout, fmt, vp, *vals++);
	if (fsp->full_data_cmnts) {
	    Printf(sout);
	    Printf(",");
	    annotate (vp, fsp, cor, iel);
	} else {
	    (void) strcat(sout, ", ");
	    lput(sout);
	}
    }
    printsval(sout, fmt, vp, *vals++);
    if (fsp->full_data_cmnts) {
	Printf(sout);
	lastdelim (more, lastrow);
	annotate (vp, fsp, cor, iel);
    } else {
	lput(sout);
	lastdelim2 (more, lastrow);
    }
}




/*
 * Print a number of int variable values, where the optional comments
 * for each value identify the variable, and each dimension index.
 */
static void
pr_ivals(
     const ncvar_t *vp,		/* variable */
     size_t len,		/* number of values to print */
     const char *fmt,		/* printf format used for each value.  If
				 * nc_type is NC_CHAR and this is NULL,
				 * character arrays will be printed as
				 * strings enclosed in quotes.  */
     boolean more,		/* true if more data for this row will
				 * follow, so add trailing comma */
     boolean lastrow,		/* true if this is the last row for this
				 * variable, so terminate with ";" instead
				 * of "," */
     const int *vals,		/* pointer to block of values */
     const fspec_t* fsp,	        /* formatting specs */
     const size_t *cor		/* corner coordinates */
     )
{
    long iel;
    char sout[100];		/* temporary string for each encoded output */

    for (iel = 0; iel < len-1; iel++) {
	printival(sout, fmt, vp, *vals++);
	if (fsp->full_data_cmnts) {
	    Printf(sout);
	    Printf(",");
	    annotate (vp, fsp, cor, iel);
	} else {
	    (void) strcat(sout, ", ");
	    lput(sout);
	}
    }
    printival(sout, fmt, vp, *vals++);
    if (fsp->full_data_cmnts) {
	Printf(sout);
	lastdelim (more, lastrow);
	annotate (vp, fsp, cor, iel);
    } else {
	lput(sout);
	lastdelim2 (more, lastrow);
    }
}


/*
 * Print a number of float variable values, where the optional comments
 * for each value identify the variable, and each dimension index.
 */
static void
pr_fvals(
     const ncvar_t *vp,		/* variable */
     size_t len,			/* number of values to print */
     const char *fmt,		/* printf format used for each value.  If
				 * nc_type is NC_CHAR and this is NULL,
				 * character arrays will be printed as
				 * strings enclosed in quotes.  */
     boolean more,		/* true if more data for this row will
				 * follow, so add trailing comma */
     boolean lastrow,		/* true if this is the last row for this
				 * variable, so terminate with ";" instead
				 * of "," */
     const float *vals,		/* pointer to block of values */
     const fspec_t* fsp,	        /* formatting specs */
     const size_t *cor		/* corner coordinates */
     )
{
    long iel;
    char sout[100];		/* temporary string for each encoded output */

    for (iel = 0; iel < len-1; iel++) {
	printfval(sout, fmt, vp, *vals++);
	if (fsp->full_data_cmnts) {
	    Printf(sout);
	    Printf(",");
	    annotate (vp, fsp, cor, iel);
	} else {
	    (void) strcat(sout, ", ");
	    lput(sout);
	}
    }
    printfval(sout, fmt, vp, *vals++);
    if (fsp->full_data_cmnts) {
	Printf(sout);
	lastdelim (more, lastrow);
	annotate (vp, fsp, cor, iel);
    } else {
	lput(sout);
	lastdelim2 (more, lastrow);
    }
}


/*
 * Print a number of double variable values, where the optional comments
 * for each value identify the variable, and each dimension index.
 */
static void
pr_dvals(
     const ncvar_t *vp,		/* variable */
     size_t len,			/* number of values to print */
     const char *fmt,		/* printf format used for each value.  If
				 * nc_type is NC_CHAR and this is NULL,
				 * character arrays will be printed as
				 * strings enclosed in quotes.  */
     boolean more,		/* true if more data for this row will
				 * follow, so add trailing comma */
     boolean lastrow,		/* true if this is the last row for this
				 * variable, so terminate with ";" instead
				 * of "," */
     const double *vals,	/* pointer to block of values */
     const fspec_t* fsp,	        /* formatting specs */
     const size_t *cor		/* corner coordinates */
     )
{
    long iel;
    char sout[100];		/* temporary string for each encoded output */

    for (iel = 0; iel < len-1; iel++) {
	printdval(sout, fmt, vp, *vals++);
	if (fsp->full_data_cmnts) {
	    Printf(sout);
	    Printf(",");
	    annotate (vp, fsp, cor, iel);
	} else {
	    (void) strcat(sout, ", ");
	    lput(sout);
	}
    }
    printdval(sout, fmt, vp, *vals++);
    if (fsp->full_data_cmnts) {
	Printf(sout);
	lastdelim (more, lastrow);
	annotate (vp, fsp, cor, iel);
    } else {
	lput(sout);
	lastdelim2 (more, lastrow);
    }
}

#ifdef USE_NETCDF4
#define MAX_OUTPUT_LEN 100

/* Print a number of variable values of any type, where the optional
 * comments for each value identify the variable, and each dimension
 * index. */
static void
pr_any_vals(const ncvar_t *vp, size_t len, const char *fmt, 
            boolean more, boolean lastrow, void *vals,
            const fspec_t* fsp, const size_t *cor, const nc_type type)
{
   long iel;
   char sout[MAX_OUTPUT_LEN]; /* temporary string for each encoded output */
   
   /* Print eah value. */
   for (iel = 0; iel < len-1; iel++) {
      print_any_val(sout, fmt, vp, type, vals);
      if (fsp->full_data_cmnts) {
	 Printf(sout);
	 Printf(",");
	 annotate (vp, fsp, cor, iel);
      } else {
	 (void) strcat(sout, ", ");
	 lput(sout);
      }
   }
   print_any_val(sout, fmt, vp, type, vals);

   if (fsp->full_data_cmnts) {
      Printf(sout);
      lastdelim (more, lastrow);
      annotate (vp, fsp, cor, iel);
   } else {
      lput(sout);
      lastdelim2 (more, lastrow);
   }
}
#endif /* USE_NETCDF4 */

/*
 * Updates a vector of ints, odometer style.  Returns 0 if odometer
 * overflowed, else 1.
 */
static int
upcorner(
     const size_t *dims,	/* The "odometer" limits for each dimension */
     int ndims,			/* Number of dimensions */
     size_t* odom,		/* The "odometer" vector to be updated */
     const size_t* add		/* A vector to "add" to odom on each update */
     )
{
    int id;
    int ret = 1;

    for (id = ndims-1; id > 0; id--) {
	odom[id] += add[id];
	if(odom[id] >= dims[id]) {
	    odom[id-1]++;
	    odom[id] -= dims[id];
	}
    }
    odom[0] += add[0];
    if (odom[0] >= dims[0])
      ret = 0;
    return ret;
}


/* Output the data for a single variable, in CDL syntax. */
int
vardata(
     const ncvar_t *vp,	/* variable */
     size_t vdims[],		/* variable dimension sizes */
     int ncid,			/* netcdf id */
     int varid,			/* variable id */
     const fspec_t* fsp	        /* formatting specs */
     )
{
    size_t cor[NC_MAX_DIMS];	/* corner coordinates */
    size_t edg[NC_MAX_DIMS];	/* edges of hypercube */
    size_t add[NC_MAX_DIMS];	/* "odometer" increment to next "row"  */
#define VALBUFSIZ 1000
    double vals[VALBUFSIZ] ; /* aligned buffer */

    int gulp = VALBUFSIZ;

    int id;
    int ir;
    size_t nels;
    size_t ncols;
    size_t nrows;
    int vrank = vp->ndims;
    static int initeps = 0;

    /* printf format used to print each value */
    const char *fmt = get_fmt(ncid, varid, vp->type);

    if (!initeps) {		/* make sure epsilons get initialized */
	init_epsilons();
	initeps = 1;
    }

    nels = 1;
    for (id = 0; id < vrank; id++) {
	cor[id] = 0;
	edg[id] = 1;
	nels *= vdims[id];	/* total number of values for variable */
    }

    if (vrank <= 1) {
	Printf("\n %s = ", vp->name);
	set_indent ((int)strlen(vp->name) + 4);
    } else {
	Printf("\n %s =\n  ", vp->name);
	set_indent (2);
    }

    if (vrank < 1) {
	ncols = 1;
    } else {
	ncols = vdims[vrank-1];	/* size of "row" along last dimension */
	edg[vrank-1] = vdims[vrank-1];
	for (id = 0; id < vrank; id++)
	  add[id] = 0;
	if (vrank > 1)
	  add[vrank-2] = 1;
    }
    nrows = nels/ncols;		/* number of "rows" */
    
    for (ir = 0; ir < nrows; ir++) {
	/*
	 * rather than just printing a whole row at once (which might exceed
	 * the capacity of MSDOS platforms, for example), we break each row
	 * into smaller chunks, if necessary.
	 */
	size_t corsav;
	int left = (int)ncols;
	boolean lastrow;

	if (vrank > 0) {
	    corsav = cor[vrank-1];
	    if (fsp->brief_data_cmnts != false
		&& vrank > 1
		&& left > 0) {	/* print brief comment with indices range */
		Printf("// %s(",vp->name);
		switch (fsp->data_lang) {
		  case LANG_C:
		    /* print brief comment with C variable indices */
		    for (id = 0; id < vrank-1; id++)
		      Printf("%lu,", (unsigned long)cor[id]);
		    if (vdims[vrank-1] == 1)
		      Printf("0");
		    else
		      Printf(" 0-%lu", (unsigned long)vdims[vrank-1]-1);
		    break;
		  case LANG_F:
		    /* print brief comment with Fortran variable indices */
		    if (vdims[vrank-1] == 1)
		      Printf("1");
		    else
		      Printf("1-%lu ", (unsigned long)vdims[vrank-1]);
		    for (id = vrank-2; id >=0 ; id--) {
			Printf(",%lu", (unsigned long)(1 + cor[id]));
		    }
		    break;
		}
		Printf(")\n    ");
		set_indent(4);
	    }
	}
	lastrow = (boolean)(ir == nrows-1);
	while (left > 0) {
	    size_t toget = left < gulp ? left : gulp;
	    if (vrank > 0)
	      edg[vrank-1] = toget;
	    switch(vp->type) {
	    case NC_CHAR:
		NC_CHECK(
		    nc_get_vara_text(ncid, varid, cor, edg, (char *)vals) );
	        pr_tvals(vp, toget, fmt, left > toget, lastrow,
			 (char *) vals, fsp, cor);
		break;
	    case NC_BYTE:
		NC_CHECK(
		    nc_get_vara_schar(ncid, varid, cor, edg, (signed char *)vals) );
	        pr_bvals(vp, toget, fmt, left > toget, lastrow,
			 (signed char *) vals, fsp, cor);
		break;
	    case NC_SHORT:
		NC_CHECK(
		    nc_get_vara_short(ncid, varid, cor, edg, (short *)vals) );
	        pr_svals(vp, toget, fmt, left > toget, lastrow,
			 (short *) vals, fsp, cor);
		break;
	    case NC_INT:
		NC_CHECK(
		    nc_get_vara_int(ncid, varid, cor, edg, (int *)vals) );
	        pr_ivals(vp, toget, fmt, left > toget, lastrow,
			 (int *) vals, fsp, cor);
		break;
	    case NC_FLOAT:
		NC_CHECK(
		    nc_get_vara_float(ncid, varid, cor, edg, (float *)vals) );
	        pr_fvals(vp, toget, fmt, left > toget, lastrow,
			 (float *) vals, fsp, cor);
		break;
	    case NC_DOUBLE:
		NC_CHECK(
		    nc_get_vara_double(ncid, varid, cor, edg, (double *)vals) );
	        pr_dvals(vp, toget, fmt, left > toget, lastrow,
			 (double *) vals, fsp, cor);
		break;
#ifdef USE_NETCDF4
	    case NC_INT64:
		NC_CHECK( nc_get_vara(ncid, varid, cor, edg, vals) );
	        pr_any_vals(vp, toget, fmt, left > toget, lastrow,
			    vals, fsp, cor, vp->type);
		break;
#endif /* USE_NETCDF4 */
	    default:
		error("vardata: bad type");
	    }
	    left -= toget;
	    if (vrank > 0)
	      cor[vrank-1] += toget;
	}
	if (vrank > 0)
	  cor[vrank-1] = corsav;
	if (ir < nrows-1)
	  if (!upcorner(vdims,vp->ndims,cor,add))
	    error("vardata: odometer overflowed!");
	set_indent(2);
    }

    return 0;
}


/*
 * print last delimiter in each line before annotation (, or ;)
 */
static void
lastdelim2x (boolean more, boolean lastrow)
{
    if (more) {
	lput(" ");
    } else {
	if(lastrow) {
	    lput("\n   ");
	} else {
	    lput("\n     ");
	}
    }
}


/*
 * Print a number of char variable values.
 */
static void
pr_tvalsx(
     size_t len,		/* number of values to print */
     const char *fmt,		/* printf format used for each value.  If
				 * nc_type is NC_CHAR and this is NULL,
				 * character arrays will be printed as
				 * strings enclosed in quotes.  */
     boolean more,		/* true if more data for this row will
				 * follow, so add trailing comma */
     boolean lastrow,		/* true if this is the last row for this
				 * variable, so terminate with ";" instead
				 * of "," */
     const char *vals,		/* pointer to block of values */
     const fspec_t *fsp	        /* formatting specs */
     )
{
    long iel;
    const char *sp;
    unsigned char uc;
    char sout[100];		/* temporary string for each encoded output */

    if (fmt == 0 || STREQ(fmt,"%s") || STREQ(fmt,"")) { /* as string */
	Printf("\"");
	/* adjust len so trailing nulls don't get printed */
	sp = vals + len;
	while (len != 0 && *--sp == '\0')
	    len--;
	for (iel = 0; iel < len; iel++)
	    switch (uc = *vals++ & 0377) {
	    case '\b':
		Printf("\\b");
		break;
	    case '\f':
		Printf("\\f");
		break;
	    case '\n':	/* generate linebreaks after new-lines */
		Printf("\\n\",\n    \"");
		break;
	    case '\r':
		Printf("\\r");
		break;
	    case '\t':
		Printf("\\t");
		break;
	    case '\v':
		Printf("\\v");
		break;
	    case '\\':
		Printf("\\\\");
		break;
	    case '\'':
		Printf("\\\'");
		break;
	    case '\"':
		Printf("\\\"");
		break;
	    default:
		if (isprint(uc))
		    Printf("%c",uc);
		else
		    Printf("\\%.3o",uc);
		break;
	    }
	Printf("\"");
    } else {		/* use format from C_format attribute */
	for (iel = 0; iel < len-1; iel++) {
	    (void) sprintf(sout, fmt, *vals++);
	    (void) strcat(sout, " ");
	    lput(sout);
	}
	(void) sprintf(sout, fmt, *vals++);
	lput(sout);
    }
    if (!fsp->full_data_cmnts) {
	lastdelim2x (more, lastrow);
    }
}


/*
 * Print a number of byte variable values.
 */
static void
pr_bvalsx(
     const ncvar_t *vp,	/* variable */
     size_t len,		/* number of values to print */
     const char *fmt,		/* printf format used for each value.  If
				 * nc_type is NC_CHAR and this is NULL,
				 * character arrays will be printed as
				 * strings enclosed in quotes.  */
     boolean more,		/* true if more data for this row will
				 * follow, so add trailing comma */
     boolean lastrow,		/* true if this is the last row for this
				 * variable, so terminate with ";" instead
				 * of "," */
     const signed char *vals	/* pointer to block of values */
     )
{
    long iel;
    char sout[100];		/* temporary string for each encoded output */

    for (iel = 0; iel < len-1; iel++) {
	printbval(sout, fmt, vp, *vals++);
	(void) strcat(sout, " ");
	lput(sout);
    }
    printbval(sout, fmt, vp, *vals++);
    lput(sout);
    lastdelim2x (more, lastrow);
}


/*
 * Print a number of short variable values.
 */
static void
pr_svalsx(
     const ncvar_t *vp,		/* variable */
     size_t len,		/* number of values to print */
     const char *fmt,		/* printf format used for each value.  If
				 * nc_type is NC_CHAR and this is NULL,
				 * character arrays will be printed as
				 * strings enclosed in quotes.  */
     boolean more,		/* true if more data for this row will
				 * follow, so add trailing comma */
     boolean lastrow,		/* true if this is the last row for this
				 * variable, so terminate with ";" instead
				 * of "," */
     const short *vals		/* pointer to block of values */
     )
{
    long iel;
    char sout[100];		/* temporary string for each encoded output */

    for (iel = 0; iel < len-1; iel++) {
	printsval(sout, fmt, vp, *vals++);
	(void) strcat(sout, " ");
	lput(sout);
    }
    printsval(sout, fmt, vp, *vals++);
    lput(sout);
    lastdelim2x (more, lastrow);
}




/*
 * Print a number of int variable values.
 */
static void
pr_ivalsx(
     const ncvar_t *vp,		/* variable */
     size_t len,		/* number of values to print */
     const char *fmt,		/* printf format used for each value.  If
				 * nc_type is NC_CHAR and this is NULL,
				 * character arrays will be printed as
				 * strings enclosed in quotes.  */
     boolean more,		/* true if more data for this row will
				 * follow, so add trailing comma */
     boolean lastrow,		/* true if this is the last row for this
				 * variable, so terminate with ";" instead
				 * of "," */
     const int *vals		/* pointer to block of values */
     )
{
    long iel;
    char sout[100];		/* temporary string for each encoded output */

    for (iel = 0; iel < len-1; iel++) {
	printival(sout, fmt, vp, *vals++);
	(void) strcat(sout, " ");
	lput(sout);
    }
    printival(sout, fmt, vp, *vals++);
    lput(sout);
    lastdelim2x (more, lastrow);
}


/*
 * Print a number of float variable values.
 */
static void
pr_fvalsx(
     const ncvar_t *vp,		/* variable */
     size_t len,			/* number of values to print */
     const char *fmt,		/* printf format used for each value.  If
				 * nc_type is NC_CHAR and this is NULL,
				 * character arrays will be printed as
				 * strings enclosed in quotes.  */
     boolean more,		/* true if more data for this row will
				 * follow, so add trailing comma */
     boolean lastrow,		/* true if this is the last row for this
				 * variable, so terminate with ";" instead
				 * of "," */
     const float *vals		/* pointer to block of values */
     )
{
    long iel;
    char sout[100];		/* temporary string for each encoded output */

    for (iel = 0; iel < len-1; iel++) {
	printfval(sout, fmt, vp, *vals++);
	(void) strcat(sout, " ");
	lput(sout);
    }
    printfval(sout, fmt, vp, *vals++);
    lput(sout);
    lastdelim2x (more, lastrow);
}


/*
 * Print a number of double variable values.
 */
static void
pr_dvalsx(
     const ncvar_t *vp,		/* variable */
     size_t len,			/* number of values to print */
     const char *fmt,		/* printf format used for each value.  If
				 * nc_type is NC_CHAR and this is NULL,
				 * character arrays will be printed as
				 * strings enclosed in quotes.  */
     boolean more,		/* true if more data for this row will
				 * follow, so add trailing comma */
     boolean lastrow,		/* true if this is the last row for this
				 * variable, so terminate with ";" instead
				 * of "," */
     const double *vals		/* pointer to block of values */
     )
{
    long iel;
    char sout[100];		/* temporary string for each encoded output */

    for (iel = 0; iel < len-1; iel++) {
	printdval(sout, fmt, vp, *vals++);
	(void) strcat(sout, " ");
	lput(sout);
    }
    printdval(sout, fmt, vp, *vals++);
    lput(sout);
    lastdelim2x (more, lastrow);
}


/* Output the data for a single variable, in NcML syntax. */
int
vardatax(
     const ncvar_t *vp,	/* variable */
     size_t vdims[],		/* variable dimension sizes */
     int ncid,			/* netcdf id */
     int varid,			/* variable id */
     const fspec_t *fsp	        /* formatting specs */
     )
{
    size_t cor[NC_MAX_DIMS];	/* corner coordinates */
    size_t edg[NC_MAX_DIMS];	/* edges of hypercube */
    size_t add[NC_MAX_DIMS];	/* "odometer" increment to next "row"  */
#define VALBUFSIZ 1000
    double vals[VALBUFSIZ] ; /* aligned buffer */

    int gulp = VALBUFSIZ;

    int id;
    int ir;
    size_t nels;
    size_t ncols;
    size_t nrows;
    int vrank = vp->ndims;
    static int initeps = 0;

    /* printf format used to print each value */
    const char *fmt = get_fmt(ncid, varid, vp->type);

    if (!initeps) {		/* make sure epsilons get initialized */
	init_epsilons();
	initeps = 1;
    }

    nels = 1;
    for (id = 0; id < vrank; id++) {
	cor[id] = 0;
	edg[id] = 1;
	nels *= vdims[id];	/* total number of values for variable */
    }

    Printf("    <values>\n     ");
    set_indent (7);

    if (vrank < 1) {
	ncols = 1;
    } else {
	ncols = vdims[vrank-1];	/* size of "row" along last dimension */
	edg[vrank-1] = vdims[vrank-1];
	for (id = 0; id < vrank; id++)
	  add[id] = 0;
	if (vrank > 1)
	  add[vrank-2] = 1;
    }
    nrows = nels/ncols;		/* number of "rows" */
    
    for (ir = 0; ir < nrows; ir++) {
	/*
	 * rather than just printing a whole row at once (which might exceed
	 * the capacity of MSDOS platforms, for example), we break each row
	 * into smaller chunks, if necessary.
	 */
	size_t corsav;
	int left = (int)ncols;
	boolean lastrow;

	if (vrank > 0) {
	    corsav = cor[vrank-1];
	}
	lastrow = (boolean)(ir == nrows-1);
	while (left > 0) {
	    size_t toget = left < gulp ? left : gulp;
	    if (vrank > 0)
	      edg[vrank-1] = toget;
	    switch(vp->type) {
	    case NC_CHAR:
		NC_CHECK(
		    nc_get_vara_text(ncid, varid, cor, edg, (char *)vals) );
	        pr_tvalsx(toget, fmt, left > toget, lastrow,
			 (char *) vals, fsp);
		break;
	    case NC_BYTE:
		NC_CHECK(
		    nc_get_vara_schar(ncid, varid, cor, edg, (signed char *)vals) );
	        pr_bvalsx(vp, toget, fmt, left > toget, lastrow,
			 (signed char *) vals);
		break;
	    case NC_SHORT:
		NC_CHECK(
		    nc_get_vara_short(ncid, varid, cor, edg, (short *)vals) );
	        pr_svalsx(vp, toget, fmt, left > toget, lastrow,
			 (short *) vals);
		break;
	    case NC_INT:
		NC_CHECK(
		    nc_get_vara_int(ncid, varid, cor, edg, (int *)vals) );
	        pr_ivalsx(vp, toget, fmt, left > toget, lastrow,
			 (int *) vals);
		break;
	    case NC_FLOAT:
		NC_CHECK(
		    nc_get_vara_float(ncid, varid, cor, edg, (float *)vals) );
	        pr_fvalsx(vp, toget, fmt, left > toget, lastrow,
			 (float *) vals);
		break;
	    case NC_DOUBLE:
		NC_CHECK(
		    nc_get_vara_double(ncid, varid, cor, edg, (double *)vals) );
	        pr_dvalsx(vp, toget, fmt, left > toget, lastrow,
			 (double *) vals);
		break;
#ifdef USE_NETCDF4
	    case NC_UBYTE:
		/* TODO */
		break;
	    case NC_USHORT:
		/* TODO */
		break;
	    case NC_UINT:
		/* TODO */
		break;
	    case NC_INT64:
		/* TODO */
		break;
	    case NC_UINT64:
		/* TODO */
		break;
	    case NC_STRING:
		/* TODO */
		break;
	    case NC_VLEN:
		/* TODO */
		break;
	    case NC_OPAQUE:
		/* TODO */
		break;
	    case NC_COMPOUND:
		/* TODO */
		break;
#endif /* USE_NETCDF4 */
	    default:
		error("vardata: bad type");
	    }
	    left -= toget;
	    if (vrank > 0)
	      cor[vrank-1] += toget;
	}
	if (vrank > 0)
	  cor[vrank-1] = corsav;
	if (ir < nrows-1)
	  if (!upcorner(vdims,vp->ndims,cor,add))
	    error("vardata: odometer overflowed!");
	set_indent(2);
    }
    Printf(" </values>\n");
    return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1