/********************************************************************* * Copyright 2018, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. *********************************************************************/ #include "d4includes.h" #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef _MSC_VER #include #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) != NCU_OK) 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;idims);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;isort != 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 upto, 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;iname); 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; } 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 strncat(tmp,"XXXXXX", sizeof(tmp) - strlen(tmp) - 1); /* 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); strncat(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 strncat */ if(uri->host != NULL) { strncat(space,uri->host,len); if(uri->port != NULL) { strncat(space,":",len); strncat(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 strncat */ if(uri->user != NULL && uri->password != NULL) { strncat(space,uri->user,len); strncat(space,":",len); strncat(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)); }