mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-11-21 03:13:42 +08:00
496 lines
11 KiB
C
496 lines
11 KiB
C
/*********************************************************************
|
|
* Copyright 2018, UCAR/Unidata
|
|
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
|
|
*********************************************************************/
|
|
|
|
#include "d4includes.h"
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#ifdef HAVE_FCNTL_H
|
|
#include <fcntl.h>
|
|
#endif
|
|
#ifdef _MSC_VER
|
|
#include <io.h>
|
|
#endif
|
|
|
|
extern int mkstemp(char *template);
|
|
|
|
#define LBRACKET '['
|
|
#define RBRACKET ']'
|
|
|
|
/**************************************************/
|
|
/* Forward */
|
|
|
|
static char* backslashEscape(const char* s);
|
|
|
|
/**************************************************/
|
|
/**
|
|
* Provide a hidden interface to allow utilities
|
|
* to check if a given path name is really an ncdap4 url.
|
|
* If no, return null, else return basename of the url
|
|
* minus any extension.
|
|
*/
|
|
|
|
int
|
|
ncd4__testurl(const char* path, char** basenamep)
|
|
{
|
|
NCURI* uri;
|
|
int ok = NC_NOERR;
|
|
if(ncuriparse(path,&uri))
|
|
ok = NC_EURL;
|
|
else {
|
|
char* slash = (uri->path == NULL ? NULL : strrchr(uri->path, '/'));
|
|
char* dot;
|
|
if(slash == NULL) slash = (char*)path; else slash++;
|
|
slash = nulldup(slash);
|
|
if(slash == NULL)
|
|
dot = NULL;
|
|
else
|
|
dot = strrchr(slash, '.');
|
|
if(dot != NULL && dot != slash) *dot = '\0';
|
|
if(basenamep)
|
|
*basenamep=slash;
|
|
else if(slash)
|
|
free(slash);
|
|
}
|
|
ncurifree(uri);
|
|
return ok;
|
|
}
|
|
|
|
/* Return 1 if this machine is little endian */
|
|
int
|
|
NCD4_isLittleEndian(void)
|
|
{
|
|
union {
|
|
unsigned char bytes[SIZEOF_INT];
|
|
int i;
|
|
} u;
|
|
u.i = 1;
|
|
return (u.bytes[0] == 1 ? 1 : 0);
|
|
}
|
|
|
|
/* Compute the size of an atomic type, except opaque */
|
|
size_t
|
|
NCD4_typesize(nc_type tid)
|
|
{
|
|
switch(tid) {
|
|
case NC_BYTE: case NC_UBYTE: case NC_CHAR: return 1;
|
|
case NC_SHORT: case NC_USHORT: return sizeof(short);
|
|
case NC_INT: case NC_UINT: return sizeof(int);
|
|
case NC_FLOAT: return sizeof(float);
|
|
case NC_DOUBLE: return sizeof(double);
|
|
case NC_INT64: case NC_UINT64: return sizeof(long long);
|
|
case NC_STRING: return sizeof(char*);
|
|
default: break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
d4size_t
|
|
NCD4_dimproduct(NCD4node* node)
|
|
{
|
|
int i;
|
|
d4size_t product = 1;
|
|
for(i=0;i<nclistlength(node->dims);i++) {
|
|
NCD4node* dim = (NCD4node*)nclistget(node->dims,i);
|
|
product *= dim->dim.size;
|
|
}
|
|
return product;
|
|
}
|
|
|
|
/* Caller must free return value */
|
|
char*
|
|
NCD4_makeFQN(NCD4node* node)
|
|
{
|
|
char* fqn = NULL;
|
|
char* escaped;
|
|
int i;
|
|
NCD4node* g = node;
|
|
NClist* path = nclistnew();
|
|
size_t estimate;
|
|
|
|
for(estimate=0;g != NULL;g=g->container) {
|
|
estimate += strlen(g->name);
|
|
nclistinsert(path,0,g);
|
|
}
|
|
estimate = (estimate*2) + 2*nclistlength(path);
|
|
estimate++; /*strlcat nul*/
|
|
fqn = (char*)malloc(estimate+1);
|
|
if(fqn == NULL) goto done;
|
|
fqn[0] = '\0';
|
|
/* Create the group-based fqn prefix */
|
|
/* start at 1 to avoid dataset */
|
|
for(i=1;i<nclistlength(path);i++) {
|
|
NCD4node* elem = (NCD4node*)nclistget(path,i);
|
|
if(elem->sort != NCD4_GROUP) break;
|
|
/* Add in the group name */
|
|
escaped = backslashEscape(elem->name);
|
|
if(escaped == NULL) {free(fqn); fqn = NULL; goto done;}
|
|
strlcat(fqn,"/",estimate);
|
|
strlcat(fqn,escaped,estimate);
|
|
free(escaped);
|
|
}
|
|
/* Add in the final name part (if not group) */
|
|
if(i < nclistlength(path)) {
|
|
int last = nclistlength(path)-1;
|
|
NCD4node* n = (NCD4node*)nclistget(path,last);
|
|
char* name = NCD4_makeName(n,".");
|
|
strlcat(fqn,"/",estimate);
|
|
strlcat(fqn,name,estimate);
|
|
nullfree(name);
|
|
}
|
|
|
|
done:
|
|
nclistfree(path);
|
|
return fqn;
|
|
}
|
|
|
|
/*
|
|
create the last part of the fqn
|
|
(post groups)
|
|
*/
|
|
char*
|
|
NCD4_makeName(NCD4node* elem, const char* sep)
|
|
{
|
|
int i;
|
|
size_t estimate = 0;
|
|
NCD4node* n;
|
|
NClist* path = nclistnew();
|
|
char* fqn = NULL;
|
|
|
|
/* Collect the path up to, but not including, the first containing group */
|
|
for(estimate=0,n=elem;n->sort != NCD4_GROUP;n=n->container) {
|
|
nclistinsert(path,0,n);
|
|
estimate += (1+(2*strlen(n->name)));
|
|
}
|
|
estimate++; /*strlcat nul*/
|
|
fqn = (char*)malloc(estimate+1);
|
|
if(fqn == NULL) goto done;
|
|
fqn[0] = '\0';
|
|
|
|
for(i=0;i<nclistlength(path);i++) {
|
|
NCD4node* elem = (NCD4node*)nclistget(path,i);
|
|
char* escaped = backslashEscape(elem->name);
|
|
if(escaped == NULL) {free(fqn); fqn = NULL; goto done;}
|
|
if(i > 0)
|
|
strlcat(fqn,sep,estimate);
|
|
strlcat(fqn,escaped,estimate);
|
|
free(escaped);
|
|
}
|
|
done:
|
|
nclistfree(path);
|
|
return fqn;
|
|
}
|
|
|
|
static char*
|
|
backslashEscape(const char* s)
|
|
{
|
|
const char* p;
|
|
char* q;
|
|
size_t len;
|
|
char* escaped = NULL;
|
|
|
|
len = strlen(s);
|
|
escaped = (char*)malloc(1+(2*len)); /* max is everychar is escaped */
|
|
if(escaped == NULL) return NULL;
|
|
for(p=s,q=escaped;*p;p++) {
|
|
char c = *p;
|
|
switch (c) {
|
|
case '\\':
|
|
case '/':
|
|
case '.':
|
|
case '@':
|
|
*q++ = '\\'; *q++ = '\\';
|
|
break;
|
|
default: *q++ = c; break;
|
|
}
|
|
}
|
|
*q = '\0';
|
|
return escaped;
|
|
}
|
|
|
|
/* Parse an fqn into a sequence of names;
|
|
using '/', and then (conditionally) '.' */
|
|
int
|
|
NCD4_parseFQN(const char* fqn0, NClist* pieces)
|
|
{
|
|
int ret = NC_NOERR;
|
|
int count;
|
|
char* p;
|
|
char* start;
|
|
char* fqn = NULL;
|
|
|
|
if(fqn0 == NULL) fqn0 = "/";
|
|
fqn = strdup(fqn0[0] == '/' ? fqn0+1 : fqn0);
|
|
start = fqn;
|
|
/* Step 0: insert rootname */
|
|
nclistpush(pieces,strdup("/"));
|
|
/* Step 1: Break fqn into pieces at occurrences of '/' */
|
|
count = 0;
|
|
for(p=start;*p;) {
|
|
switch(*p) {
|
|
case '\\': /* leave the escapes in place */
|
|
p+=2;
|
|
break;
|
|
case '/': /*capture the piece name */
|
|
*p++ = '\0';
|
|
start = p; /* mark start of the next part */
|
|
count++;
|
|
break;
|
|
default: /* ordinary char */
|
|
p++;
|
|
break;
|
|
}
|
|
}
|
|
#ifdef ALLOWFIELDMAPS
|
|
/* Step 2, walk the final piece to break up based on '.' */
|
|
for(p=start;*p;) {
|
|
switch(*p) {
|
|
case '\\': /* leave the escapes in place */
|
|
p+=2;
|
|
break;
|
|
case '.': /*capture the piece name */
|
|
*p++ = '\0';
|
|
start = p;
|
|
count++;
|
|
break;
|
|
default: /* ordinary char */
|
|
p++;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
count++; /* acct for last piece */
|
|
/* Step 3: capture and de-scape the pieces */
|
|
for(p=fqn;count > 0;count--) {
|
|
char* descaped = NCD4_deescape(p);
|
|
nclistpush(pieces,descaped);
|
|
p = p + strlen(p) + 1; /* skip past the terminating nul */
|
|
}
|
|
if(fqn != NULL) free(fqn);
|
|
return THROW(ret);
|
|
}
|
|
|
|
char*
|
|
NCD4_deescape(const char* esc)
|
|
{
|
|
size_t len;
|
|
char* s;
|
|
const char* p;
|
|
char* q;
|
|
|
|
if(esc == NULL) return NULL;
|
|
len = strlen(esc);
|
|
s = (char*)malloc(len+1);
|
|
if(s == NULL) return NULL;
|
|
for(p=esc,q=s;*p;) {
|
|
switch (*p) {
|
|
case '\\':
|
|
p++;
|
|
/* fall thru */
|
|
default: *q++ = *p++; break;
|
|
}
|
|
}
|
|
*q = '\0';
|
|
return s;
|
|
}
|
|
|
|
char*
|
|
NCD4_entityescape(const char* s)
|
|
{
|
|
const char* p;
|
|
char* q;
|
|
size_t len;
|
|
char* escaped = NULL;
|
|
const char* entity;
|
|
|
|
len = strlen(s);
|
|
escaped = (char*)malloc(1+(6*len)); /* 6 = |'| */
|
|
if(escaped == NULL) return NULL;
|
|
for(p=s,q=escaped;*p;p++) {
|
|
char c = *p;
|
|
switch (c) {
|
|
case '&': entity = "&"; break;
|
|
case '<': entity = "<"; break;
|
|
case '>': entity = ">"; break;
|
|
case '"': entity = """; break;
|
|
case '\'': entity = "'"; break;
|
|
default : entity = NULL; break;
|
|
}
|
|
if(entity == NULL)
|
|
*q++ = c;
|
|
else {
|
|
len = strlen(entity);
|
|
memcpy(q,entity,len);
|
|
q+=len;
|
|
}
|
|
}
|
|
*q = '\0';
|
|
return escaped;
|
|
}
|
|
|
|
/* Elide all nul characters from an XML document as a precaution*/
|
|
size_t
|
|
NCD4_elidenuls(char* s, size_t slen)
|
|
{
|
|
size_t i,j;
|
|
for(j=0,i=0;i<slen;i++) {
|
|
int c = s[i];
|
|
if(c != 0)
|
|
s[j++] = (char)c;
|
|
}
|
|
/* if we remove any nuls then nul term */
|
|
if(j < i)
|
|
s[j] = '\0';
|
|
return j;
|
|
}
|
|
|
|
int
|
|
NCD4_readfile(const char* filename, NCbytes* content)
|
|
{
|
|
int ret = NC_NOERR;
|
|
FILE* stream = NULL;
|
|
char part[1024];
|
|
|
|
stream = fopen(filename,"r");
|
|
if(stream == NULL) {ret=errno; goto done;}
|
|
for(;;) {
|
|
size_t count = fread(part, 1, sizeof(part), stream);
|
|
if(count <= 0) break;
|
|
ncbytesappendn(content,part,count);
|
|
if(ferror(stream)) {ret = NC_EIO; goto done;}
|
|
if(feof(stream)) break;
|
|
}
|
|
ncbytesnull(content);
|
|
done:
|
|
if(stream) fclose(stream);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
Wrap mktmp and return the generated name
|
|
*/
|
|
|
|
int
|
|
NCD4_mktmp(const char* base, char** tmpnamep)
|
|
{
|
|
int fd;
|
|
char tmp[NC_MAX_PATH];
|
|
#ifdef HAVE_MKSTEMP
|
|
mode_t mask;
|
|
#endif
|
|
|
|
strncpy(tmp,base,sizeof(tmp));
|
|
#ifdef HAVE_MKSTEMP
|
|
strlcat(tmp,"XXXXXX", sizeof(tmp));
|
|
/* Note Potential problem: old versions of this function
|
|
leave the file in mode 0666 instead of 0600 */
|
|
mask=umask(0077);
|
|
fd = mkstemp(tmp);
|
|
(void)umask(mask);
|
|
#else /* !HAVE_MKSTEMP */
|
|
/* Need to simulate by using some kind of pseudo-random number */
|
|
{
|
|
int rno = rand();
|
|
char spid[7];
|
|
if(rno < 0) rno = -rno;
|
|
snprintf(spid,sizeof(spid),"%06d",rno);
|
|
strlcat(tmp,spid,sizeof(tmp));
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
fd=open(tmp,O_RDWR|O_BINARY|O_CREAT, _S_IREAD|_S_IWRITE);
|
|
# else
|
|
fd=open(tmp,O_RDWR|O_CREAT|O_EXCL, S_IRWXU);
|
|
# endif
|
|
}
|
|
#endif /* !HAVE_MKSTEMP */
|
|
if(fd < 0) {
|
|
nclog(NCLOGERR, "Could not create temp file: %s",tmp);
|
|
return THROW(NC_EPERM);
|
|
} else
|
|
close(fd);
|
|
if(tmpnamep) *tmpnamep = strdup(tmp);
|
|
return THROW(NC_NOERR);
|
|
}
|
|
|
|
void
|
|
NCD4_hostport(NCURI* uri, char* space, size_t len)
|
|
{
|
|
if(space != NULL && len > 0) {
|
|
space[0] = '\0'; /* so we can use strlcat */
|
|
if(uri->host != NULL) {
|
|
strlcat(space,uri->host,len);
|
|
if(uri->port != NULL) {
|
|
strlcat(space,":",len);
|
|
strlcat(space,uri->port,len);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
NCD4_userpwd(NCURI* uri, char* space, size_t len)
|
|
{
|
|
if(space != NULL && len > 0) {
|
|
space[0] = '\0'; /* so we can use strlcat */
|
|
if(uri->user != NULL && uri->password != NULL) {
|
|
strlcat(space,uri->user,len);
|
|
strlcat(space,":",len);
|
|
strlcat(space,uri->password,len);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************************/
|
|
/* Error reporting */
|
|
|
|
int
|
|
NCD4_error(int code, const int line, const char* file, const char* fmt, ...)
|
|
{
|
|
va_list argv;
|
|
fprintf(stderr,"(%s:%d) ",file,line);
|
|
va_start(argv,fmt);
|
|
ncvlog(NCLOGERR,fmt,argv);
|
|
return code;
|
|
}
|
|
|
|
int
|
|
NCD4_errorNC(int code, const int line, const char* file)
|
|
{
|
|
return NCD4_error(code,line,file,nc_strerror(code));
|
|
}
|
|
|
|
d4size_t
|
|
NCD4_getcounter(void* p)
|
|
{
|
|
COUNTERTYPE v;
|
|
memcpy(&v,p,sizeof(v));
|
|
return (d4size_t)v;
|
|
}
|
|
|
|
void*
|
|
NCD4_getheader(void* p, NCD4HDR* hdr, int hostlittleendian)
|
|
{
|
|
unsigned char bytes[4];
|
|
memcpy(bytes,p,sizeof(bytes));
|
|
p = INCR(p,4); /* on-the-wire hdr is 4 bytes */
|
|
/* assume header is network (big) order */
|
|
hdr->flags = bytes[0]; /* big endian => flags are in byte 0 */
|
|
hdr->flags &= NCD4_ALL_CHUNK_FLAGS; /* Ignore extraneous flags */
|
|
bytes[0] = 0; /* so we can do byte swap to get count */
|
|
if(hostlittleendian)
|
|
swapinline32(bytes); /* host is little endian */
|
|
hdr->count = *(unsigned int*)bytes; /* get count */
|
|
return p;
|
|
}
|
|
|
|
void
|
|
NCD4_reporterror(NCD4INFO* state)
|
|
{
|
|
NCD4meta* meta = state->substrate.metadata;
|
|
char* u = NULL;
|
|
if(meta == NULL) return;
|
|
u = ncuribuild(state->uri,NULL,NULL,NCURIALL);
|
|
fprintf(stderr,"***FAIL: url=%s httpcode=%d errmsg->\n%s\n",u,meta->error.httpcode,meta->error.message);
|
|
}
|