netcdf-c/ncdump/ocprint.c
2018-12-12 11:47:54 -07:00

1192 lines
32 KiB
C

/* Copyright 2018, UCAR/Unidata and OPeNDAP, Inc.
See the COPYRIGHT file for more information. */
#define VALIDATE
#define ALLATONCE
#undef TRACK
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <assert.h>
#include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include "netcdf.h"
#include "ncuri.h"
#include "ncbytes.h"
#include "nclog.h"
#include "oc.h"
#include "ocx.h"
#ifdef _WIN32
#include "XGetopt.h"
int opterr, optind;
char* optarg;
#endif
#ifndef nulldup
#define nulldup(s) (s==NULL?NULL:strdup(s))
#endif
#define CHECK(x) check_err((ocstat=(x)),0)
#define FAIL(x) check_err((x),1)
/* Define some classifiers */
#define iscontainer(t) ((t) == OC_Dataset || (t) == OC_Structure || (t) == OC_Sequence || (t) == OC_Grid)
#define isatomic(t) ((t) == OC_Atomic)
#define NORC "NONE"
#define LBRACE "{"
#define RBRACE "}"
/*Mnemonic*/
#define TOPLEVEL 1
int ocdebug;
static OCerror ocstat;
static OClink glink;
/* define a large stack of DUMPPATH datanodes */
struct DUMPPATH {
OCdatanode datanode;
OCddsnode node;
OCtype octype;
size_t rank;
size_t dimsizes[OC_MAX_DIMENSIONS];
int indexed; /* 1 => indices is valid */
size_t indices[OC_MAX_DIMENSIONS];
} stack[2048];
static size_t stacknext;
/* Define the debug options */
struct OCD {
int debug; /* -D<integer:1..> */
int debuglevel;
int dumpdds; /* -DN */
int dumpdatadds; /* -DX<level 0..> */
int dumpdatatree; /* -DD */
int dumplevel;
int curl; /* -DC - make curl be verbose */
int verbose; /* -DV - produce more verbose output */
} debug;
/* Define the -X options; currently unused*/
struct OCX {
int ignore;
} x;
/* Define the other options */
static struct OCOPT {
char* surl; /* full url string */
NCURI* url;
struct OCD debug;
struct OCX x;
int showattributes; /* -A */
int logging; /* -L */
char* netrc ; /* -N */
char* rcfile ; /* -R */
int selfsigned ; /* -S */
int octest; /* -T */ /* match original octest output */
int generate; /* -g|-G */
int optdas; /* -p */
int optdatadds; /* -p */
int optdds; /* -p */
FILE* output; /* -o */
/* Deprecated */
char* constraint; /* -C */
NCbytes* userparams; /* -U */
} ocopt;
static char blanks[2048];
#define BLANKSPERDENT 2
/* Forward*/
static void usage(char*);
static int fail(char*);
static void check_err(OCerror stat, int dofail);
static void dumpflags(void);
struct OCOPT;
static OCerror processdata(int);
static OCerror printdata(OClink, OCdatanode);
static OCerror printdata_indices(OClink, OCdatanode, NCbytes*,int);
static OCerror printdata_container(OClink, OCdatanode, NCbytes*,int);
static OCerror printdata_leaf(OClink, OCdatanode, NCbytes*,int);
static off_t odom_init(size_t rank, size_t* indices, size_t* dimsizes);
static int odom_more(size_t rank, size_t* indices, size_t* dimsizes);
static void odom_next(size_t rank, size_t* indices, size_t* dimsizes);
static OCerror dumpdatanode(OClink, OCdatanode, size_t count, void* memory, NCbytes*);
static OCerror generatedds(OClink, OCddsnode, NCbytes*, int depth);
static char* generatedas(OClink,OCddsnode);
static OCerror generatedasr(OClink, OCddsnode, NCbytes*, int depth);
static OCerror generateddsattributes(OClink, OCddsnode node, NCbytes*, int);
static OCerror printdims(OClink link, OCddsnode node, NCbytes* buffer);
static char* stringescape(char*);
static char* idescape(char*, char*, size_t);
static int needsescapes(const char* s);
static size_t totaldimsize(size_t,size_t*);
static char* indent(int n);
static void pushstack(OCdatanode datanode);
static void popstack() {stacknext--;}
#ifdef TRACK
static void printstack(char* msg);
#endif
static char* optionmsg =
" [-A]"
" [-D <debugarg>]"
" [-G]"
" [-L]"
" [-N <netrc file>]"
" [-S]"
" [-R <rcfile>]"
" [-T]"
" [-h]"
" [-o <file|->]"
" [-p das|dds|datadds]"
" <url>";
static OCflags ocflags;
EXTERNL int nc_initialize(void);
static void
init()
{
memset(&ocopt,0,sizeof(ocopt));
ocopt.generate = 1; /* -G|-g */
ocopt.userparams = ncbytesnew(); /* -U */
nc_initialize();
}
int
main(int argc, char **argv)
{
int c;
char* suffix;
#ifdef OCDEBUG
{ int i;
fprintf(stderr,"argv =");
for(i=0;i<argc;i++)
fprintf(stderr," %s",argv[i]);
fprintf(stderr,"\n");
}
#endif
init();
opterr = 1;
while ((c = getopt(argc, argv, "AC:D:GLN:R:STU:X:gho:u:f:p:")) != EOF) {
switch (c) {
case 'A': ocopt.showattributes = 1; break;
case 'C': ocopt.constraint = nulldup(optarg); break;
case 'G': case 'g': ocopt.generate = 1; break;
case 'L': ocopt.logging = 1; break;
case 'N': ocopt.netrc = nulldup(optarg); break;
case 'R': ocopt.rcfile = nulldup(optarg); break;
case 'S': ocopt.selfsigned = 1; break;
case 'T': ocopt.octest = 1; break;
case 'U':
if(optarg != NULL) {
ncbytesappend(ocopt.userparams,';');
ncbytescat(ocopt.userparams,optarg);
}
break;
case 'D': {
int c0;
if(optarg == NULL || strlen(optarg) == 0) usage("missing -D argument");
c0 = optarg[0];
if(c0 >= '0' && c0 <= '9') {/* debug level */
ocopt.debug.debuglevel = atoi(optarg); break;
} else switch (c0) {
case 'C': ocopt.debug.curl = 1; break;
case 'D': ocopt.debug.dumpdatatree = 1; break;
case 'N': ocopt.debug.dumpdds = 1; break;
case 'X': ocopt.debug.dumpdatadds = 1;
ocopt.debug.dumplevel = atoi(optarg+1);
break;
case 'V': ocopt.debug.verbose = 1; break;
default: usage("unknown -D option");
}
} break;
case 'X': {
int c0;
int so = (optarg == NULL ? 0 : strlen(optarg));
if(so == 0) usage("missing -X argument");
c0 = optarg[0];
switch (c0) {
default:
usage("unknown -X option");
}
} break;
case 'o':
if(ocopt.output != NULL) fclose(ocopt.output);
if(optarg == NULL)
usage("-o does not specify a file name");
ocopt.output = fopen(optarg,"w");
if(ocopt.output == NULL)
usage("-o file not writeable");
break;
case 'u': case 'f':
ocopt.surl = optarg;
break;
case 'p':
if(optarg == NULL)
usage("-p does not specify an argument");
if(strcasecmp(optarg,"das")==0) ocopt.optdas=1;
else if(strcasecmp(optarg,"dds")==0) ocopt.optdds=1;
else if(strcasecmp(optarg,"data")==0) ocopt.optdatadds=1;
else if(strcasecmp(optarg,"datadds")==0) ocopt.optdatadds=1;
else if(strcasecmp(optarg,"all")==0) {
ocopt.optdas = 1; ocopt.optdds = 1; ocopt.optdatadds = 1;
} else usage("unknown -p option");
break;
case 'h': usage(""); exit(0);
default: usage("unknown option");
}
}
if(ocopt.output == NULL)
ocopt.output = stdout;
if (ocopt.debug.debuglevel > 0) {
ocdebug = ocopt.debug.debuglevel;
}
if(ocopt.logging) {
ncloginit();
ncsetlogging(1);
if(!nclogopen(NULL))
fprintf(stderr,"Failed to open logging output\n");
}
argc -= optind;
argv += optind;
if (argc > 0 && ocopt.surl== NULL) {
ocopt.surl = nulldup(argv[argc - 1]);
} else
usage("Multiple urls specified");
if (ocopt.surl == NULL)
ocopt.surl = getenv("URLSRC");
if (ocopt.surl == NULL) {
usage("no source url specified\n");
}
/* Compile the url */
if(ncuriparse(ocopt.surl,&ocopt.url) != NCU_OK) {
fprintf(stderr,"malformed source url: %s\n",ocopt.surl);
exit(1);
}
/* For convenience, see if the url has a trailing .dds, .das, or .dods
and if so, use it
*/
suffix = strrchr(ocopt.url->path,'.');
if(suffix != NULL) {
int match = 0;
if(strcmp(suffix,".das")==0) {
ocopt.optdas = 1;
ocopt.optdds = 0;
ocopt.optdatadds = 0;
match = 1;
} else if(strcmp(suffix,".dds")==0) {
ocopt.optdas = 0;
ocopt.optdds = 1;
ocopt.optdatadds = 0;
match = 1;
} else if(strcmp(suffix,".dods")==0) {
ocopt.optdas = 0;
ocopt.optdds = 0;
ocopt.optdatadds = 1;
match = 1;
}
/* Remove the suffix */
if(match)
*suffix = '\0';
}
/* If -C was specified, then it has precedence */
if(ocopt.constraint != NULL) {
ncurisetquery(ocopt.url,ocopt.constraint);
nullfree(ocopt.constraint);
ocopt.constraint = NULL;
}
/* Rebuild the url string */
if(ocopt.surl != NULL) free(ocopt.surl);
ocopt.surl = ncuribuild(ocopt.url,NULL,NULL,NCURIALL);
/* Reparse */
if(ncuriparse(ocopt.surl,&ocopt.url) != NCU_OK) {
fprintf(stderr,"malformed source url: %s\n",ocopt.surl);
exit(1);
}
if(ocopt.rcfile != NULL) {
}
if (ocopt.debug.verbose)
dumpflags();
processdata(ocflags);
return 0;
}
static void
dumpflags(void)
{
char* tmp;
if(ocopt.showattributes) fprintf(stderr," -A");
if(ocopt.debug.debug) fprintf(stderr," -D%d",ocopt.debug.debuglevel);
if(ocopt.debug.dumpdds) fprintf(stderr," -DN");
if(ocopt.debug.dumpdatatree) fprintf(stderr," -DD");
if(ocopt.debug.dumpdatadds) fprintf(stderr," -DX%d",ocopt.debug.dumplevel);
if(ocopt.debug.verbose) fprintf(stderr," -DV");
if(ocopt.generate) fprintf(stderr," -G");
if(ocopt.logging) fprintf(stderr," -L");
if(ocopt.logging) fprintf(stderr," -N %s",ocopt.netrc);
if(ocopt.logging) fprintf(stderr," -R %s",ocopt.rcfile);
if(ocopt.optdas || ocopt.optdds || ocopt.optdatadds) {
fprintf(stderr," -p");
if(ocopt.optdas) fprintf(stderr," das");
if(ocopt.optdds) fprintf(stderr," dds");
if(ocopt.optdatadds) fprintf(stderr," datadds");
}
tmp = ncuribuild(ocopt.url,NULL,NULL,NCURIALL);
fprintf(stderr,"%s\n",tmp);
free(tmp);
}
static void
usage(char* msg)
{
if(msg) fprintf(stderr,"error: %s\n",msg);
fprintf(stderr,"usage: ocprint %s\n",optionmsg);
fail(NULL);
}
static int
fail(char* msg)
{
if(msg) fprintf(stderr,"fatalerror: %s\n",msg);
fflush(ocopt.output); fflush(stderr);
exit(1);
}
/******************************************/
static void
check_err(OCerror ocstat, int dofail)
{
if(ocstat == OC_NOERR) return;
fprintf(stderr,"error status returned: (%d) %s\n",ocstat,oc_errstring(ocstat));
if(dofail) fail(NULL);
}
static OCerror
processdata(OCflags flags)
{
char* totalurl;
OClink link;
OCddsnode dasroot, ddsroot, dataddsroot;
OCdatanode rootdatanode;
totalurl = ncuribuild(ocopt.url,NULL,NULL,NCURIALL);
FAIL(oc_open(totalurl,&link));
free(totalurl);
glink = link;
if(ocopt.debug.curl)
oc_trace_curl(link);
if(ocopt.netrc)
oc_set_netrc(link,ocopt.netrc);
#if 0
if(ocopt.selfsigned)
oc_set_curlopt(link,"CURLOPT_VERIFYPEER", (void*)0L);
#endif
if(ocopt.optdas) {
ocstat = oc_fetch(link,ocopt.url->query,OCDAS,0,&dasroot);
if(ocstat != OC_NOERR) {
fprintf(stderr,"error status returned: (%d) %s\n",ocstat,oc_errstring(ocstat));
fprintf(stderr,"Could not read DAS; continuing.\n");
ocopt.optdas = 0;
ocopt.showattributes = 0;
} else if(ocopt.generate) {
char* das = generatedas(link,dasroot);
fprintf(ocopt.output,"%s",das);
free(das);
} else {
const char* text = oc_tree_text(link,dasroot);
fprintf(ocopt.output,"%s",(text?text:"null"));
}
}
fflush(ocopt.output);
if(ocopt.optdds) {
ocstat = oc_fetch(link,ocopt.url->query,OCDDS,flags,&ddsroot);
if(ocstat != OC_NOERR) {
fprintf(stderr,"error status returned: (%d) %s\n",ocstat,oc_errstring(ocstat));
fprintf(stderr,"Could not read DDS; continuing.\n");
ocopt.optdds = 0;
} else {
if(ocopt.showattributes && !ocopt.optdas) {
FAIL(oc_fetch(link,ocopt.url->query,OCDAS,flags,&dasroot));
}
if(ocopt.showattributes || ocopt.optdas) {
FAIL(oc_merge_das(link,dasroot,ddsroot));
}
if(ocopt.generate) {
NCbytes* buffer = ncbytesnew();
FAIL(generatedds(link,ddsroot,buffer,0));
fprintf(ocopt.output,"%s",ncbytescontents(buffer));
ncbytesfree(buffer);
} else {
const char* text = oc_tree_text(link,ddsroot);
fprintf(ocopt.output,"%s",(text?text:"null"));
}
}
if(ocopt.debug.dumpdds)
oc_dds_ddnode(link,ddsroot);
}
fflush(ocopt.output);
if(ocopt.optdatadds) {
ocstat = oc_fetch(link,ocopt.url->query,OCDATADDS,flags,&dataddsroot);
if(ocstat) {
fprintf(stderr,"Cannot read DATADDS: %s\n",ocopt.surl);
exit(1);
}
if(ocopt.debug.dumpdds)
oc_dds_ddnode(link,dataddsroot);
if(ocopt.debug.dumpdatadds)
oc_dds_dd(link,dataddsroot,ocopt.debug.dumplevel);
FAIL(oc_dds_getdataroot(link,dataddsroot,&rootdatanode));
if(ocopt.debug.dumpdatatree)
oc_data_ddtree(link,rootdatanode);
stacknext = 0;
printdata(link,rootdatanode);
}
fflush(ocopt.output);
oc_close(link);
return OC_NOERR;
}
/**
This is the main procedure for
printing a data tree.
*/
static OCerror
printdata(OClink link, OCdatanode datanode)
{
NCbytes* buffer;
OCtype octype;
buffer = ncbytesnew();
/* verify that datanode is a Dataset datanode */
FAIL(oc_data_octype(link,datanode,&octype));
assert(octype == OC_Dataset);
printdata_container(link,datanode,buffer,TOPLEVEL);
fprintf(ocopt.output,"%s",ncbytescontents(buffer));
ncbytesfree(buffer);
return OC_NOERR;
}
/* Print a single container datanode */
static OCerror
printdata_container(OClink link, OCdatanode datanode, NCbytes* buffer, int istoplevel)
{
OCerror stat = OC_NOERR;
size_t i;
OCddsnode node;
OCtype octype;
size_t nsubnodes;
/* Obtain some information about the node */
FAIL(oc_data_ddsnode(link,datanode,&node));
FAIL(oc_dds_nsubnodes(link,node,&nsubnodes));
FAIL(oc_data_octype(link,datanode,&octype));
/* If this is not a single instance container, then
defer to the appropriate function */
if(isatomic(octype))
return printdata_leaf(link,datanode,buffer,istoplevel);
if(oc_data_indexable(link,datanode))
return printdata_indices(link,datanode,buffer,!TOPLEVEL);
/* Must be a container instance or a record */
for(i=0;i<nsubnodes;i++) {
OCdatanode field;
FAIL(oc_data_ithfield(link,datanode,i,&field));
pushstack(field);
FAIL(printdata_indices(link,field,buffer,istoplevel));
popstack();
if(oc_data_free(link,field) != OC_NOERR)
break;
}
return stat;
}
static OCerror
printdata_indices(OClink link, OCdatanode datanode, NCbytes* buffer, int istoplevel)
{
OCerror stat = OC_NOERR;
size_t i;
OCddsnode node;
size_t rank;
OCtype octype;
size_t dimsizes[OC_MAX_DIMENSIONS];
size_t indices[OC_MAX_DIMENSIONS];
/* Obtain some information about the node */
FAIL(oc_data_ddsnode(link,datanode,&node));
FAIL(oc_dds_octype(link,node,&octype));
FAIL(oc_dds_rank(link,node,&rank));
/* If this is not an indexed structure or a sequence then
defer to the appropriate function */
if(isatomic(octype))
return printdata_leaf(link,datanode,buffer,istoplevel);
if(iscontainer(octype) && !oc_data_indexable(link,datanode))
return printdata_container(link,datanode,buffer,istoplevel);
/* Iterate over the datanodes */
if(octype == OC_Structure) {
/* Get dimension sizes */
FAIL(oc_dds_dimensionsizes(link,node,dimsizes));
/* init odometer and get cross-product */
odom_init(rank,indices,dimsizes);
while(odom_more(rank,indices,dimsizes)) {
OCdatanode element;
FAIL(oc_data_ithelement(link,datanode,indices,&element));
pushstack(element);
/* walk the container */
printdata_container(link,element,buffer,!TOPLEVEL);
popstack();
oc_data_free(link,element);
odom_next(rank,indices,dimsizes);
}
} else if(octype == OC_Sequence) {
/* Dump each element */
for(i=0;;i++) {
OCdatanode record;
stat = oc_data_ithrecord(link,datanode,i,&record);
if(stat != OC_NOERR) {
if(stat == OC_EINDEX) break;
return stat;
}
pushstack(record);
printdata_container(link,record,buffer,!TOPLEVEL); /* print current record */
popstack();
oc_data_free(link,record);
}
#ifdef VALIDATE
{
size_t count;
FAIL(oc_data_recordcount(link,datanode,&count));
assert(count == i);
}
#endif
} else
abort();
return OC_NOERR;
}
static OCerror
printdata_leaf(OClink link, OCdatanode datanode, NCbytes* buffer, int istoplevel)
{
OCddsnode node;
OCtype octype,atomtype;
size_t elemsize;
size_t memsize;
char* memory;
size_t count,rank;
/* Obtain some information about the node */
FAIL(oc_data_ddsnode(link,datanode,&node));
FAIL(oc_dds_octype(link,node,&octype));
FAIL(oc_dds_atomictype(link,node,&atomtype));
FAIL(oc_dds_rank(link,node,&rank));
assert(octype == OC_Atomic);
/* If this variable is top-level then
use the oc_dds_read functions
in order to test them
*/
elemsize = oc_typesize(atomtype);
if(rank == 0) {/* Scalar case */
memory = calloc(elemsize,1); /* reading only one value */
/* read the scalar */
if(istoplevel) {
FAIL(oc_dds_read(link,node,NULL,NULL,elemsize,memory));
} else {
FAIL(oc_data_read(link,datanode,NULL,NULL,elemsize,memory));
}
count = 1;
} else {
size_t dimsizes[OC_MAX_DIMENSIONS];
size_t indices[OC_MAX_DIMENSIONS];
FAIL(oc_dds_dimensionsizes(link,node,dimsizes));
/* init odometer and get cross-product */
count = odom_init(rank,indices,dimsizes);
memsize = elemsize*count;
memory = calloc(memsize,1);
#ifdef ALLATONCE /* read all at once */
/* indices should be all zeros at this point */
if(istoplevel) {
FAIL(oc_dds_read(link,node,indices,dimsizes,memsize,memory));
} else {
FAIL(oc_data_read(link,datanode,indices,dimsizes,memsize,memory));
}
#else /* BYITEM */
{
size_t offset;
size_t one[OC_MAX_DIMENSIONS];
/* Initialize the read-by-one counts */
for(i=0;i<rank;i++) one[i]=0;
one[rank-1] = 1;
/* Read whole atomic array item by item using an odometer */
for(offset=0,i=0;i<count;i++,offset+=elemsize) {
if(!odom_more(rank,indices,dimsizes))
abort();
if(istoplevel) {
FAIL(oc_dds_read(link,node,
indices,one,
elemsize,memory+offset));
} else {
FAIL(oc_data_read(link,datanode,
indices,one,
elemsize,memory+offset));
}
odom_next(rank,indices,dimsizes);
}
}
#endif
}
dumpdatanode(link,datanode,count,memory,buffer);
if(atomtype == OC_String || atomtype == OC_URL)
oc_reclaim_strings(count,(char**)memory);
free(memory);
return OC_NOERR;
}
static OCerror
generatedds(OClink link, OCddsnode node, NCbytes* buffer, int depth)
{
size_t i,rank,nattr,nsubnodes;
OCtype octype, atomtype;
OCddsnode container,field;
char id1[1024];
char* name;
ncbytescat(buffer,indent(depth));
/* get all info about the node */
FAIL(oc_dds_properties(link,node,&name,&octype,&atomtype,&container,
&rank,&nsubnodes,&nattr));
if(octype == OC_Atomic) {
ncbytescat(buffer,oc_typetostring(atomtype));
ncbytescat(buffer," ");
ncbytescat(buffer,idescape(name,id1,sizeof(id1)));
/* dump dim info (if any)*/
printdims(link,node,buffer);
ncbytescat(buffer,";\n");
generateddsattributes(link,node,buffer,depth+1);
} else { /*must be container*/
const char* typename = oc_typetostring(octype);
ncbytescat(buffer,typename);
ncbytescat(buffer," ");
ncbytescat(buffer,LBRACE);
ncbytescat(buffer,"\n");
for(i=0;i<nsubnodes;i++) {
FAIL(oc_dds_ithfield(link,node,i,&field));
if(octype == OC_Grid) {
ncbytescat(buffer,indent(depth));
switch (i) {
case 0: ncbytescat(buffer,"Array:\n"); break;
case 1: ncbytescat(buffer,"Maps:\n"); break;
default: break;
}
}
generatedds(link,field,buffer,depth+1);
}
ncbytescat(buffer,indent(depth));
ncbytescat(buffer,RBRACE);
ncbytescat(buffer," ");
ncbytescat(buffer,idescape(name,id1,sizeof(id1)));
printdims(link,node,buffer);
ncbytescat(buffer,";\n");
generateddsattributes(link,node,buffer,depth+1);
}
if(name) free(name);
return OC_NOERR;
}
static OCerror
printdims(OClink link, OCddsnode node, NCbytes* buffer)
{
int i;
size_t rank,size;
OCddsnode dimids[OC_MAX_DIMENSIONS];
char tmp[1024];
char id1[1024];
FAIL(oc_dds_rank(link,node,&rank));
if(rank == 0) return OC_NOERR;
FAIL(oc_dds_dimensions(link,node,dimids));
for(i=0;i<rank;i++) {
OCddsnode dim = dimids[i];
char* dimname = NULL;
FAIL(oc_dimension_properties(link,dim,&size,&dimname));
if(dimname == NULL)
snprintf(tmp,sizeof(tmp),"[%lu]",(unsigned long)size);
else
snprintf(tmp,sizeof(tmp),"[%s=%lu]",idescape(dimname,id1,sizeof(id1)),(unsigned long)size);
ncbytescat(buffer,tmp);
if(dimname) free(dimname);
}
return OC_NOERR;
}
static OCerror
generateddsattributes(OClink link, OCddsnode node, NCbytes* buffer, int depth)
{
size_t i,j;
char tmp[128];
size_t nattrs;
char* aname = NULL;
char* name = NULL;
OCtype atomtype;
size_t nvalues;
char** values = NULL;
char id1[1024];
FAIL(oc_dds_attr_count(link,node,&nattrs));
FAIL(oc_dds_name(link,node,&name));
if(ocopt.showattributes && nattrs > 0) {
for(i=0;i<nattrs;i++) {
FAIL(oc_dds_attr(link,node,i,NULL,NULL,&nvalues,NULL));
values = (char**)malloc(nvalues*sizeof(char*));
FAIL(oc_dds_attr(link,node,i,&aname,&atomtype,NULL,values));
snprintf(tmp,sizeof(tmp),"%s%s %s:%s = ",indent(depth),
oc_typetostring(atomtype),idescape(name,id1,sizeof(id1)),aname);
ncbytescat(buffer,tmp);
for(j=0;j<nvalues;j++) {
if(j > 0) ncbytescat(buffer,", ");
if(needsescapes(values[j])) {
char* escaped = stringescape(values[j]);
ncbytescat(buffer,"\"");
ncbytescat(buffer,escaped);
ncbytescat(buffer,"\"");
if(escaped) free(escaped);
} else
ncbytescat(buffer,values[j]);
}
ncbytescat(buffer,";\n");
oc_reclaim_strings(nvalues,values);
if(values) free(values);
if(aname) free(aname);
values = NULL;
aname = NULL;
}
}
if(name) free(name);
return OC_NOERR;
}
static char*
generatedas(OClink link, OCddsnode root)
{
size_t i, nsubnodes;
char* result;
NCbytes* buffer = ncbytesnew();
FAIL(oc_dds_nsubnodes(link,root,&nsubnodes));
ncbytescat(buffer,"Attributes {\n");
for(i=0;i<nsubnodes;i++) {
OCddsnode attr;
FAIL(oc_dds_ithfield(link,root,i,&attr));
generatedasr(link,attr,buffer,1);
}
ncbytescat(buffer,"}\n");
result = ncbytesdup(buffer);
ncbytesfree(buffer);
return result;
}
static OCerror
generatedasr(OClink link, OCddsnode ddsnode, NCbytes* buffer, int depth)
{
size_t i,nsubnodes;
char tmp[256];
OCtype octype, atomtype;
char* name = NULL;
char id1[1024];
/* get some node info */
FAIL(oc_dds_name(link,ddsnode,&name));
FAIL(oc_dds_octype(link,ddsnode,&octype));
FAIL(oc_dds_atomictype(link,ddsnode,&atomtype));
if(octype == OC_Attributeset) {
/* get node subcount */
FAIL(oc_dds_nsubnodes(link,ddsnode,&nsubnodes));
snprintf(tmp,sizeof(tmp),"%s%s {\n",indent(depth),idescape(name,id1,sizeof(id1)));
ncbytescat(buffer,tmp);
for(i=0;i<nsubnodes;i++) {
OCddsnode attr;
FAIL(oc_dds_ithfield(link,ddsnode,i,&attr));
generatedasr(link,attr,buffer,depth+1);
}
ncbytescat(buffer,indent(depth));
ncbytescat(buffer,"}\n");
} else if(octype == OC_Attribute) {
/* get some info about the node */
size_t nvalues;
FAIL(oc_das_attr_count(link,ddsnode,&nvalues));
snprintf(tmp,sizeof(tmp),"%s%s %s",indent(depth),
oc_typetostring(atomtype),idescape(name,id1,sizeof(id1)));
ncbytescat(buffer,tmp);
for(i=0;i<nvalues;i++) {
char* value;
OCtype ptype;
FAIL(oc_das_attr(link,ddsnode,i,&ptype,&value));
if(i > 0) ncbytescat(buffer,",");
if(ptype == OC_String || ptype == OC_URL) {
char* se = stringescape(value);
snprintf(tmp,sizeof(tmp)," \"%s\"",se);
free(se);
} else
snprintf(tmp,sizeof(tmp)," %s",value);
ncbytescat(buffer,tmp);
free(value);
}
ncbytescat(buffer,";\n");
} else {
snprintf(tmp,sizeof(tmp),"ocget DAS: unexpected type: %d",(int)octype);
ncbytescat(buffer,tmp);
}
if(name) free(name);
return OC_NOERR;
}
static char hexdigits[] = "0123456789abcdef";
/* Add escape characters to a string */
static char*
stringescape(char* s)
{
size_t len;
char* p;
int c;
char* escapedstring;
if(s == NULL) return NULL;
len = strlen(s);
escapedstring = (char*)malloc(4*len);
p = escapedstring;
while((c=*s++)) {
if(c == '"' || c == '\\') {*p++ = '\\'; *p++ = c;}
else if (c < ' ' || c >= 127) {
*p++ = '\\'; *p++ = 'x';
*p++ = hexdigits[(c & 0xf0)>>4];
*p++ = hexdigits[(c & 0xf)];
} else
*p++ = c;
}
*p = '\0';
return escapedstring;
}
static char idchars[] = "_%";
/* Add escape characters to an identifier */
static char*
idescape(char* id, char* escapeid, size_t esize)
{
char* p;
int c;
if(id == NULL) return NULL;
p = escapeid;
*p = '\0';
esize--; /* leave room for null */
while(esize-- > 0 && (c=*id++)) {
if(c >= '0' && c <= '9') {*p++ = c;}
else if(c >= 'a' && c <= 'z') {*p++ = c;}
else if(c >= 'A' && c <= 'Z') {*p++ = c;}
else if(strchr(idchars,c) != NULL) {*p++ = c;}
else {
*p++ = '%';
*p++ = hexdigits[(c & 0xf0)>>4];
*p++ = hexdigits[(c & 0xf)];
}
}
*p = '\0';
return escapeid;
}
static char valuechars[] = " \\\"";
/**
Return 1 if the given string, used as a value, should be escaped.
*/
static int
needsescapes(const char* s)
{
const char* p = s;
int c;
while((c=*p++)) {
if(strchr(valuechars,c) != NULL)
return 1; /* needs to be escaped */
}
return 0;
}
static OCerror
dumpdatanode(OClink link, OCdatanode datanode, size_t count, void* memory, NCbytes* buffer)
{
size_t i;
size_t delta;
OCddsnode node;
OCtype atomtype;
OCtype octype;
NCbytes* path = NULL;
char* name = NULL;
char id[1024];
char tmp[1024];
struct DUMPPATH* entry = NULL;
FAIL(oc_data_ddsnode(link,datanode,&node));
FAIL(oc_dds_octype(link,node,&octype));
FAIL(oc_dds_atomictype(link,node,&atomtype));
delta = oc_typesize(atomtype);
#ifdef TRACK
printstack("dumpdatanode");
#endif
/* construct the fully qualified name from the stack; watch out for duplicates
from e.g. sequence versus record */
path = ncbytesnew();
for(i=0;i<stacknext;i++) {
entry = stack + i;
/* check for duplicate */
if(i<(stacknext-1) && entry->node == stack[i+1].node) continue;
/* Get various pieces of additional node information */
FAIL(oc_dds_name(glink,entry->node,&name));
(void)idescape(name,id,sizeof(id));
if(name) free(name);
switch (entry->octype) {
case OC_Dataset:
break;
case OC_Structure:
ncbytescat(path,"/");
ncbytescat(path,id);
if(entry->rank > 0) {
for(i=0;i<entry->rank;i++) {
sprintf(tmp,"[%lu]",(unsigned long)entry->indices[i]);
ncbytescat(path,tmp);
}
}
break;
case OC_Grid:
ncbytescat(path,"/");
ncbytescat(path,id);
break;
case OC_Sequence:
ncbytescat(path,"/");
ncbytescat(path,id);
sprintf(tmp,"[%lu]",(unsigned long)entry->indices[0]);
ncbytescat(path,tmp);
break;
case OC_Atomic:
ncbytescat(path,"/");
ncbytescat(path,id);
break; /* deal with below */
default: ncbytescat(path,"?"); break;
}
}
/* work with the final entry */
assert(entry == (stack + (stacknext - 1)));
assert(datanode == entry->datanode);
snprintf(tmp,sizeof(tmp),"%s %s",
oc_typetostring(atomtype),
ncbytescontents(path));
ncbytescat(buffer,tmp);
if(entry->rank > 0) {
if(ocopt.octest) { /* Match the octest output */
off_t xproduct;
xproduct = totaldimsize(entry->rank,entry->dimsizes);
snprintf(tmp,sizeof(tmp),"[0..%lu]",(unsigned long)xproduct-1);
ncbytescat(buffer,tmp);
} else {
for(i=0;i<entry->rank;i++) {
snprintf(tmp,sizeof(tmp),"[0..%lu]",(unsigned long)entry->dimsizes[i]-1);
ncbytescat(buffer,tmp);
}
}
}
count = totaldimsize(entry->rank,entry->dimsizes);
for(i=0;i<count;i++) {
char *memory_local = memory;
ncbytescat(buffer," ");
oc_typeprint(atomtype,memory_local+(i*delta),sizeof(tmp),tmp);
ncbytescat(buffer,tmp);
}
ncbytescat(buffer,"\n");
ncbytesfree(path);
return OC_NOERR;
}
static off_t
odom_init(size_t rank, size_t* indices, size_t* dimsizes)
{
int i;
off_t count;
for(count=1,i=0;i<rank;i++) {
indices[i] = 0;
count *= dimsizes[i];
}
return count;
}
static void
odom_next(size_t rank, size_t* indices, size_t* dimsizes)
{
int i;
for(i=rank-1;i>=0;i--) {
indices[i]++;
if(indices[i] < dimsizes[i]) break;
if(i > 0) indices[i] = 0;
}
}
/* Return 0 if we have exhausted the indices, 1 otherwise */
static int
odom_more(size_t rank, size_t* indices, size_t* dimsizes)
{
if(indices[0] >= dimsizes[0]) return 0;
return 1;
}
/* Compute total # of elements if dimensioned */
static size_t
totaldimsize(size_t rank, size_t* sizes)
{
unsigned int i;
size_t count = 1;
for(i=0;i<rank;i++) {
count *= sizes[i];
}
return count;
}
static char*
indent(int n)
{
size_t nblanks = BLANKSPERDENT * n;
memset(blanks,' ',nblanks);
blanks[nblanks] = '\0';
return blanks;
}
static void
pushstack(OCdatanode datanode)
{
struct DUMPPATH* entry = stack+stacknext;
entry->datanode = datanode;
FAIL(oc_data_ddsnode(glink,entry->datanode,&entry->node));
FAIL(oc_dds_octype(glink,entry->node,&entry->octype));
FAIL(oc_dds_rank(glink,entry->node,&entry->rank));
if(entry->rank > 0) {
FAIL(oc_dds_dimensionsizes(glink,entry->node,entry->dimsizes));
}
entry->indexed = oc_data_indexed(glink,entry->datanode);
if(entry->indexed) {
FAIL(oc_data_position(glink,entry->datanode,entry->indices));
}
stacknext++;
}
#ifdef TRACK
static void printstack(char* msg)
{
size_t i,j;
struct DUMPPATH* entry;
fprintf(stderr,"\n%s@stack: %u\n",msg,stacknext);
for(entry=stack,i=0;i<stacknext;i++,entry++) {
OCdatanode datanode = entry->datanode;
OCddsnode node;
size_t rank;
size_t edges[OC_MAX_DIMENSIONS];
char* name;
FAIL(oc_dds_rank(glink,entry->node,&rank));
if(entry->rank > 0)
FAIL(oc_dds_dimensionsizes(glink,entry->node,edges));
FAIL(oc_dds_name(glink,node,&name));
fprintf(stderr," [%d] (%s)",(int)i,name)
for(j=0;j<rank;j++)
fprintf(stderr,"[%u]",(unsigned int)edges[j]);
fprintf(stderr,"\n");
}
}
#endif