#include "netcdf.h" #include "api.h" #include "buffer.h" #include #include #include #include #define NC_TYPE 'NC' #define GLOBAL_ATTNAME "General" #pragma warning(disable: 4018) #pragma warning(disable: 4244) class zsRegPrimitive { public: zsRegPrimitive(const char* name, int type, void* func) { api_add_primitive(name, type, func); } }; class zsNetcdf { size_t size; public: zsNetcdf() : ncid(0), varid(0), ndim(0), vsize(0), esize(0), size(0), ptr(0) { for (int i = 0; i < NC_MAX_VAR_DIMS; i++) { dimid[i] = 0; dims[i] = 0; } } ~zsNetcdf() { nc_close(ncid); if (ptr) free(ptr); } void checksize(size_t size) { if (this->size < size || this->size > 2*size) { if (ptr) { free(ptr); ptr = 0; } ptr = malloc(size); this->size = size; } } int ncid, varid, ndim, dimid[NC_MAX_VAR_DIMS]; size_t vsize, esize, dims[NC_MAX_VAR_DIMS]; nc_type type; void *ptr; }; ///////////////////////////////////////////////////////////////////// void input_error(void* caller) { api_runtime_error(caller, "bad arguments"); } void type_error(void* caller) { api_runtime_error(caller, "unexpected data type"); } void cdf_check_error(void* caller, int status) { if (status != NC_NOERR) api_runtime_error(caller, nc_strerror(status)); } void check_variable(void* caller, zsNetcdf *o) { if (o->ndim == 0) api_runtime_error(caller, "varaible not specified"); } void cdf_destroy(void* ptr) { delete (zsNetcdf*)ptr; } ///////////////////////////////////////////////////////////////////// void* cdf_open(void *caller, int nargs, void** args) { if (nargs < 1) input_error(caller); const char *fname = api_get_string(caller, args[0]); const char *mode = "r"; zsNetcdf *o = new zsNetcdf; if (nargs > 1) mode = api_get_string(caller, args[1]); if (mode[0] == 'w' || mode[0] == 'W') cdf_check_error(caller, nc_open(fname, NC_WRITE, &o->ncid)); else if (mode[0] == 'c' || mode[0] == 'C') cdf_check_error(caller, nc_create(fname, NC_NOCLOBBER, &o->ncid)); else cdf_check_error(caller, nc_open(fname, NC_NOWRITE, &o->ncid)); return api_create_user(caller, o, 0, cdf_destroy, NC_TYPE); } static zsRegPrimitive nc0("netcdf", 0, cdf_open); void* cdf_version(void *caller, int nargs, void** args) { return api_create_string(caller, (char*)nc_inq_libvers()); } static zsRegPrimitive nc1("version", NC_TYPE, cdf_version); void* cdf_defdim(void *caller, int nargs, void** args) { if (nargs < 3) input_error(caller); zsNetcdf *o = (zsNetcdf*)api_get_user(caller, args[0], NC_TYPE); const char *name = api_get_string (caller, args[1]); int n = api_get_integer(caller, args[2]); nc_redef(o->ncid); if (n > 0) cdf_check_error(caller, nc_def_dim(o->ncid, name, n, &o->varid)); else cdf_check_error(caller, nc_def_dim(o->ncid, name, NC_UNLIMITED, &o->varid)); return api_create_integer(caller, o->varid); } static zsRegPrimitive nc2("defdim", NC_TYPE, cdf_defdim); void* cdf_defvar(void *caller, int nargs, void** args) { if (nargs < 4 || nargs-3 > NC_MAX_VAR_DIMS) input_error(caller); zsNetcdf *o = (zsNetcdf*)api_get_user(caller, args[0], NC_TYPE); const char *name = api_get_string(caller, args[1]); const char *type = api_get_string(caller, args[2]); if (!strcmp(type, "char")) { o->type = NC_CHAR; o->esize = sizeof(signed char); } else if (!strcmp(type, "byte")) { o->type = NC_BYTE; o->esize = sizeof(unsigned char); } else if (!strcmp(type, "uchar")) { o->type = NC_BYTE; o->esize = sizeof(unsigned char); } else if (!strcmp(type, "short")) { o->type = NC_SHORT; o->esize = sizeof(short); } else if (!strcmp(type, "int")) { o->type = NC_INT; o->esize = sizeof(int); } else if (!strcmp(type, "float")) { o->type = NC_FLOAT; o->esize = sizeof(float); } else if (!strcmp(type, "double")) { o->type = NC_DOUBLE; o->esize = sizeof(double); } else { input_error(caller); } o->ndim = 0; o->vsize = 1; for (int i = 3; i < nargs; i++) { o->dimid[o->ndim] = api_get_integer(caller, args[i]); nc_inq_dimlen(o->ncid, o->dimid[o->ndim], &o->dims[o->ndim]); o->vsize *= o->dims[o->ndim]; o->ndim++; } nc_redef(o->ncid); cdf_check_error(caller, nc_def_var(o->ncid, name, o->type, o->ndim, o->dimid, &o->varid)); return 0; } static zsRegPrimitive nc3("defvar", NC_TYPE, cdf_defvar); void get_var_info(void *caller, const char*name, zsNetcdf *o) { cdf_check_error(caller, nc_inq_varid(o->ncid, name, &o->varid)); cdf_check_error(caller, nc_inq_vartype(o->ncid, o->varid, &o->type)); cdf_check_error(caller, nc_inq_varndims(o->ncid, o->varid, &o->ndim)); cdf_check_error(caller, nc_inq_vardimid(o->ncid, o->varid, o->dimid)); o->vsize = 1; for (int i = 0; i < o->ndim; i++) { cdf_check_error(caller, nc_inq_dimlen(o->ncid, o->dimid[i], &o->dims[i])); o->vsize *= o->dims[i]; } switch (o->type) { case NC_CHAR: o->esize = sizeof(signed char); break; case NC_BYTE: o->esize = sizeof(unsigned char); break; case NC_SHORT: o->esize = sizeof(short); break; case NC_INT: o->esize = sizeof(int); break; case NC_FLOAT: o->esize = sizeof(float); break; case NC_DOUBLE: o->esize = sizeof(double); break; default: type_error(caller); } } void* cdf_variable(void *caller, int nargs, void** args) { if (nargs < 2) input_error(caller); zsNetcdf *o = (zsNetcdf*)api_get_user(caller, args[0], NC_TYPE); const char *name = api_get_string(caller, args[1]); o->ndim = 0; if (nc_inq_varid(o->ncid, name, &o->varid) == NC_NOERR) { get_var_info(caller, name, o); return api_create_integer(caller, 1); } return api_create_integer(caller, 0); } static zsRegPrimitive nc4("variable", NC_TYPE, cdf_variable); void* cdf_size(void *caller, int nargs, void** args) { if (nargs < 1) input_error(caller); zsNetcdf *o = (zsNetcdf*)api_get_user(caller, args[0], NC_TYPE); check_variable(caller, o); void *arr = api_create_array(caller, 3); api_set_array_object(caller, arr, "0", api_create_integer(caller, o->vsize)); api_set_array_object(caller, arr, "1", api_create_integer(caller, o->esize)); switch (o->type) { case NC_CHAR: api_set_array_object(caller, arr, "2", api_create_string(caller, "char")); break; case NC_BYTE: api_set_array_object(caller, arr, "2", api_create_string(caller, "uchar")); break; case NC_SHORT: api_set_array_object(caller, arr, "2", api_create_string(caller, "short")); break; case NC_INT: api_set_array_object(caller, arr, "2", api_create_string(caller, "int")); break; case NC_FLOAT: api_set_array_object(caller, arr, "2", api_create_string(caller, "float")); break; case NC_DOUBLE: api_set_array_object(caller, arr, "2", api_create_string(caller, "double")); break; default: type_error(caller); } return arr; } static zsRegPrimitive nc5("size", NC_TYPE, cdf_size); void* cdf_dims(void *caller, int nargs, void** args) { if (nargs < 1) input_error(caller); zsNetcdf *o = (zsNetcdf*)api_get_user(caller, args[0], NC_TYPE); check_variable(caller, o); void *arr = api_create_array(caller, o->ndim); for (int i = 0; i < o->ndim; i++) { api_set_array_object2(caller, arr, i, api_create_integer(caller, o->dims[i])); } return arr; } static zsRegPrimitive nc6("dims", NC_TYPE, cdf_dims); void* cdf_getatt(void *caller, int nargs, void** args) { if (nargs < 2) input_error(caller); zsNetcdf *o = (zsNetcdf*)api_get_user(caller, args[0], NC_TYPE); const char *name = api_get_string(caller, args[1]); nc_enddef(o->ncid); nc_type type; size_t size; if (strcmp(name, "GLOBAL") == 0) { cdf_check_error(caller, nc_inq_att(o->ncid, NC_GLOBAL, GLOBAL_ATTNAME, &type, &size)); zsBuffer buf(size+1); cdf_check_error(caller, nc_get_att_text(o->ncid, NC_GLOBAL, GLOBAL_ATTNAME, buf.u.pchar)); buf.u.pchar[size] = 0; return api_create_string(caller, buf.u.pchar); } check_variable(caller, o); cdf_check_error(caller, nc_inq_att(o->ncid, o->varid, name, &type, &size)); switch(type) { case NC_CHAR: { zsBuffer buf(size+1); cdf_check_error(caller, nc_get_att_text(o->ncid, o->varid, name, buf.u.pchar)); buf.u.pchar[size] = 0; return api_create_string(caller, buf.u.pchar); } break; case NC_BYTE: { zsBuffer buf(size); cdf_check_error(caller, nc_get_att_uchar(o->ncid, o->varid, name, buf.u.puchar)); if (size > 1) { void *ret = api_create_array(caller, size+1); for (int i = 0; i < size; i++) { api_set_array_object2(caller, ret, i, api_create_integer(caller, buf.u.puchar[i])); } return ret; } else { return api_create_integer(caller, buf.u.puchar[0]); } } break; case NC_SHORT: { zsBuffer buf(size*sizeof(short)); cdf_check_error(caller, nc_get_att_short(o->ncid, o->varid, name, buf.u.pshort)); if (size > 1) { void *ret = api_create_array(caller, size+1); for (int i = 0; i < size; i++) { api_set_array_object2(caller, ret, i, api_create_integer(caller, buf.u.pshort[i])); } return ret; } else { return api_create_integer(caller, buf.u.pshort[0]); } } break; case NC_INT: { zsBuffer buf(size*sizeof(int)); cdf_check_error(caller, nc_get_att_int(o->ncid, o->varid, name, buf.u.pint)); if (size > 1) { void *ret = api_create_array(caller, size+1); for (int i = 0; i < size; i++) { api_set_array_object2(caller, ret, i, api_create_integer(caller, buf.u.pint[i])); } return ret; } else { return api_create_integer(caller, buf.u.pint[0]); } } break; case NC_FLOAT: { zsBuffer buf(size*sizeof(float)); cdf_check_error(caller, nc_get_att_float(o->ncid, o->varid, name, buf.u.pfloat)); if (size > 1) { void *ret = api_create_array(caller, size+1); for (int i = 0; i < size; i++) { api_set_array_object2(caller, ret, i, api_create_real(caller, buf.u.pfloat[i])); } return ret; } else { return api_create_real(caller, buf.u.pfloat[0]); } } break; case NC_DOUBLE: { zsBuffer buf(size*sizeof(float)); cdf_check_error(caller, nc_get_att_double(o->ncid, o->varid, name, buf.u.pdouble)); if (size > 1) { void *ret = api_create_array(caller, size+1); for (int i = 0; i < size; i++) { api_set_array_object2(caller, ret, i, api_create_real(caller, buf.u.pdouble[i])); } return ret; } else { return api_create_real(caller, buf.u.pdouble[0]); } } break; default: api_runtime_error(caller, "failed to read unsupported type of attribute"); } return 0; } void* cdf_setatt(void *caller, int nargs, void** args) { if (nargs < 3) input_error(caller); zsNetcdf *o = (zsNetcdf*)api_get_user(caller, args[0], NC_TYPE); const char *name = api_get_string(caller, args[1]); int offset = 2; if (strcmp(name, "GLOBAL") == 0) { nc_redef(NC_GLOBAL); nc_del_att(o->ncid, NC_GLOBAL, GLOBAL_ATTNAME); const char *str = api_get_string(caller, args[offset]); cdf_check_error(caller, nc_put_att_text(o->ncid, NC_GLOBAL, GLOBAL_ATTNAME, strlen(str), str)); return 0; } check_variable(caller, o); nc_redef(o->ncid); nc_del_att(o->ncid, o->varid, name); if (api_is_string(args[offset])) { const char *str = api_get_string(caller, args[offset]); cdf_check_error(caller, nc_put_att_text(o->ncid, o->varid, name, strlen(str), str)); return 0; } if (api_is_integer(args[offset])) { int value = api_get_integer(caller, args[offset]); cdf_check_error(caller, nc_put_att_int(o->ncid, o->varid, name, NC_INT, 1, &value)); return 0; } if (api_is_real(args[offset])) { double value = api_get_real(caller, args[offset]); cdf_check_error(caller, nc_put_att_double(o->ncid, o->varid, name, NC_DOUBLE, 1, &value)); return 0; } if (api_is_array(args[offset])) { int n = api_get_array_size(caller, args[offset]); zsBuffer bi(n*sizeof(int)), br(n*sizeof(double)); bool real_att = false; for (int i = 0; i < n; i++) { void *o = api_get_array_object2(caller, args[offset], i); if (api_is_real(o)) { br.u.pdouble[i] = api_get_real(caller, o); real_att = true; } else { bi.u.pint[i] = api_get_integer(caller, o); br.u.pdouble[i] = bi.u.pint[i]; } } if (real_att) { cdf_check_error(caller, nc_put_att_double(o->ncid, o->varid, name, NC_DOUBLE, n, br.u.pdouble)); } else { cdf_check_error(caller, nc_put_att_int(o->ncid, o->varid, name, NC_INT, n, bi.u.pint)); } return 0; } api_runtime_error(caller, "failed to write unsupported type of attribute"); return 0; } size_t cdf_check_index(void *caller, int nargs, void** args, zsNetcdf *o, size_t *start, size_t *count) { if (nargs-1 > o->ndim) api_runtime_error(caller, "too many dimension specifiers"); size_t size = 1; for (int k = 0; k < o->ndim; k++) { start[k] = 0; count[k] = o->dims[k]; size *= o->dims[k]; } for (int i = 1; i < nargs; i++) { int k = i - 1; if (!api_is_null(args[i])) { size /= o->dims[k]; if (api_is_integer(args[i])) { start[k] = api_get_integer(caller, args[i]); count[k] = 1; } else { start[k] = api_get_integer(caller, api_get_array_object(caller, args[i], "0")); count[k] = api_get_integer(caller, api_get_array_object(caller, args[i], "1")); if (count[k] < start[k]) api_runtime_error(caller, "invalid range specifiers"); count[k] = count[k] - start[k] + 1; size *= count[k]; } } } return size; } void* cdf_get(void *caller, int nargs, void** args) { if (nargs < 2) input_error(caller); if (nargs == 2 && api_is_string(args[1])) { // cdf.name return cdf_getatt(caller, nargs, args); } // cdf[...] zsNetcdf *o = (zsNetcdf*)api_get_user(caller, args[0], NC_TYPE); check_variable(caller, o); nc_enddef(o->ncid); size_t size, start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS]; size = cdf_check_index(caller, nargs, args, o, start, count); switch (o->type) { case NC_CHAR: o->checksize(size*sizeof(signed char)); cdf_check_error(caller, nc_get_vara_schar(o->ncid, o->varid, start, count, (signed char*)o->ptr)); if (size == 1) { return api_create_integer(caller, ((signed char*)o->ptr)[0]); } else { return api_create_user(caller, o->ptr, 0, 0, NC_TYPE); } case NC_BYTE: o->checksize(size*sizeof(unsigned char)); cdf_check_error(caller, nc_get_vara_uchar(o->ncid, o->varid, start, count, (unsigned char*)o->ptr)); if (size == 1) { return api_create_integer(caller, ((unsigned char*)o->ptr)[0]); } else { return api_create_user(caller, o->ptr, 0, 0, NC_TYPE); } case NC_SHORT: o->checksize(size*sizeof(short)); cdf_check_error(caller, nc_get_vara_short(o->ncid, o->varid, start, count, (short*)o->ptr)); if (size == 1) { return api_create_integer(caller, ((short*)o->ptr)[0]); } else { return api_create_user(caller, o->ptr, 0, 0, NC_TYPE); } case NC_INT: o->checksize(size*sizeof(int)); cdf_check_error(caller, nc_get_vara_int(o->ncid, o->varid, start, count, (int*)o->ptr)); if (size == 1) { return api_create_integer(caller, ((int*)o->ptr)[0]); } else { return api_create_user(caller, o->ptr, 0, 0, NC_TYPE); } case NC_FLOAT: o->checksize(size*sizeof(float)); cdf_check_error(caller, nc_get_vara_float(o->ncid, o->varid, start, count, (float*)o->ptr)); if (size == 1) { return api_create_real(caller, ((float*)o->ptr)[0]); } else { return api_create_user(caller, o->ptr, 0, 0, NC_TYPE); } case NC_DOUBLE: o->checksize(size*sizeof(double)); cdf_check_error(caller, nc_get_vara_double(o->ncid, o->varid, start, count, (double*)o->ptr)); if (size == 1) { return api_create_real(caller, ((double*)o->ptr)[0]); } else { return api_create_user(caller, o->ptr, 0, 0, NC_TYPE); } default: type_error(caller); } return 0; } static zsRegPrimitive nc7("__get", NC_TYPE, cdf_get); void* cdf_set(void *caller, int nargs, void** args) { if (nargs < 3) input_error(caller); if (nargs == 3 && api_is_string(args[1])) { // cdf.name = attribute return cdf_setatt(caller, nargs, args); } // cdf[..] = data zsNetcdf *o = (zsNetcdf*)api_get_user(caller, args[0], NC_TYPE); check_variable(caller, o); nc_enddef(o->ncid); size_t size, start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS]; nargs--; size = cdf_check_index(caller, nargs, args, o, start, count); switch (o->type) { case NC_CHAR: if (api_is_user(args[nargs])) { void *ptr = api_get_ptr(caller, args[nargs]); cdf_check_error(caller, nc_put_vara_schar(o->ncid, o->varid, start, count, (signed char*)ptr)); } else { signed char value = api_get_integer(caller, args[nargs]); zsBuffer buf(size*sizeof(signed char)); for (size_t i = 0; i < size; i++) buf.u.pschar[i] = value; cdf_check_error(caller, nc_put_vara_schar(o->ncid, o->varid, start, count, buf.u.pschar)); } break; case NC_BYTE: if (api_is_user(args[nargs])) { void *ptr = api_get_ptr(caller, args[nargs]); cdf_check_error(caller, nc_put_vara_uchar(o->ncid, o->varid, start, count, (unsigned char*)ptr)); } else { unsigned char value = api_get_integer(caller, args[nargs]); zsBuffer buf(size*sizeof(unsigned char)); for (size_t i = 0; i < size; i++) buf.u.puchar[i] = value; cdf_check_error(caller, nc_put_vara_uchar(o->ncid, o->varid, start, count, buf.u.puchar)); } break; case NC_SHORT: if (api_is_user(args[nargs])) { void *ptr = api_get_ptr(caller, args[nargs]); cdf_check_error(caller, nc_put_vara_short(o->ncid, o->varid, start, count, (short*)ptr)); } else { short value = api_get_integer(caller, args[nargs]); zsBuffer buf(size*sizeof(short)); for (size_t i = 0; i < size; i++) buf.u.pshort[i] = value; cdf_check_error(caller, nc_put_vara_short(o->ncid, o->varid, start, count, buf.u.pshort)); } break; case NC_INT: if (api_is_user(args[nargs])) { void *ptr = api_get_ptr(caller, args[nargs]); cdf_check_error(caller, nc_put_vara_int(o->ncid, o->varid, start, count, (int*)ptr)); } else { int value = api_get_integer(caller, args[nargs]); zsBuffer buf(size*sizeof(int)); for (size_t i = 0; i < size; i++) buf.u.pint[i] = value; cdf_check_error(caller, nc_put_vara_int(o->ncid, o->varid, start, count, buf.u.pint)); } break; case NC_FLOAT: if (api_is_user(args[nargs])) { void *ptr = api_get_ptr(caller, args[nargs]); cdf_check_error(caller, nc_put_vara_float(o->ncid, o->varid, start, count, (float*)ptr)); } else { float value = api_get_real(caller, args[nargs]); zsBuffer buf(size*sizeof(float)); for (size_t i = 0; i < size; i++) buf.u.pfloat[i] = value; cdf_check_error(caller, nc_put_vara_float(o->ncid, o->varid, start, count, buf.u.pfloat)); } break; case NC_DOUBLE: if (api_is_user(args[nargs])) { void *ptr = api_get_ptr(caller, args[nargs]); cdf_check_error(caller, nc_put_vara_double(o->ncid, o->varid, start, count, (double*)ptr)); } else { double value = api_get_real(caller, args[nargs]); zsBuffer buf(size*sizeof(double)); for (size_t i = 0; i < size; i++) buf.u.pdouble[i] = value; cdf_check_error(caller, nc_put_vara_double(o->ncid, o->varid, start, count, buf.u.pdouble)); } break; default: type_error(caller); } return 0; } static zsRegPrimitive nc8("__set", NC_TYPE, cdf_set); void get_attributes(void* caller, int ncid, FILE *f, const int ivar, const char* space) { int i, status, natt; char name[NC_MAX_NAME]; cdf_check_error(caller, nc_inq_varnatts(ncid, ivar, &natt)); for (i = 0; i < natt; i++) { nc_type type; size_t j, len; cdf_check_error(caller, nc_inq_attname(ncid, ivar, i, name)); cdf_check_error(caller, nc_inq_att(ncid, ivar, name, &type, &len)); zsBuffer buf(len*sizeof(double)), str(64); fprintf(f, "%s%s:", space, name); switch(type) { case NC_CHAR: status = nc_get_att_text(ncid, ivar, name, buf.u.pchar); if (status == NC_NOERR) { buf.u.pchar[len] = 0; fprintf(f, " %s", buf.u.pchar); } break; case NC_BYTE: status = nc_get_att_uchar(ncid, ivar, name, buf.u.puchar); if (status == NC_NOERR) { for (j = 0; j < len; j++) fprintf(f, " %d", buf.u.puchar[j]); } break; case NC_SHORT: status = nc_get_att_short(ncid, ivar, name, buf.u.pshort); if (status == NC_NOERR) { for (j = 0; j < len; j++) fprintf(f, " %d", buf.u.pshort[j]); } break; case NC_INT: status = nc_get_att_int(ncid, ivar, name, buf.u.pint); if (status == NC_NOERR) { for (j = 0; j < len; j++) fprintf(f, " %d", buf.u.pint[j]); } break; case NC_FLOAT: status = nc_get_att_float(ncid, ivar, name, buf.u.pfloat); if (status == NC_NOERR) { for (j = 0; j < len; j++) fprintf(f, " %s", format_real(buf.u.pfloat[j], str.u.pchar)); } break; case NC_DOUBLE: status = nc_get_att_double(ncid, ivar, name, buf.u.pdouble); if (status == NC_NOERR) { for (j = 0; j < len; j++) fprintf(f, " %s", format_real(buf.u.pdouble[j], str.u.pchar)); } break; default: api_runtime_error(caller, "nnknown data type"); } fprintf(f, "\n"); cdf_check_error(caller, status); } fflush(stdout); } void* cdf_info(void *caller, int nargs, void** args) { if (nargs < 1) input_error(caller); zsNetcdf *o = (zsNetcdf*)api_get_user(caller, args[0], NC_TYPE); int ncid = o->ncid; FILE *f = stdout; if (nargs > 1) f = fopen(api_get_string(caller, args[1]), "w"); if (!f) f = stdout; // dimensions int i, unlimited, nvar, ndim, ngatt; size_t len, dim[NC_MAX_VAR_DIMS]; char name[NC_MAX_NAME]; fprintf(f, "Dimensions:\n"); cdf_check_error(caller, nc_inq(ncid, &ndim, &nvar, &ngatt, &unlimited)); for (i = 0; i < ndim; i++) { cdf_check_error(caller, nc_inq_dim(ncid, i, name, &len)); dim[i] = len; if (unlimited == i) fprintf(f, " %s = %d (unlimited)\n", name, len); else fprintf(f, " %s = %d\n", name, len); } // variables fprintf(f, "\nVariables:\n"); for (i = 0; i < nvar; i++) { int natt, ndim, dimid[NC_MAX_VAR_DIMS]; nc_type type; cdf_check_error(caller, nc_inq_var(ncid, i, name, &type, &ndim, dimid, &natt)); switch(type) { case NC_CHAR: fprintf(f, " char"); break; case NC_BYTE: fprintf(f, " byte"); break; case NC_SHORT: fprintf(f, " short"); break; case NC_INT: fprintf(f, " int"); break; case NC_FLOAT: fprintf(f, " float"); break; case NC_DOUBLE: fprintf(f, " doule"); break; default: fprintf(f, " unknown"); break; } fprintf(f, " %s", name); if (ndim > 0) { for (int j = 0; j < ndim; j++) { cdf_check_error(caller, nc_inq_dimname(ncid, dimid[j], name)); if (j == 0) fprintf(f, "(%s", name); else fprintf(f, ", %s", name); } fprintf(f, ")"); } fprintf(f, "\n"); get_attributes(caller, ncid, f, i, " "); fprintf(f, "\n"); } // global attributes fprintf(f, "Global attributes:\n"); get_attributes(caller, ncid, f, NC_GLOBAL, " "); fflush(f); if (f != stdout) fclose(f); return 0; } static zsRegPrimitive nc9("info", NC_TYPE, cdf_info);