mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-12-03 08:01:25 +08:00
9b7202bf06
re: https://github.com/Unidata/netcdf-c/issues/2189 Compression of a variable whose type is variable length fails for all current filters. This is because at some point, the compression buffer will contain pointers to data instead of the actual data. Compression of pointers of course is meaningless. The PR changes the behavior of nc_def_var_filter so that it will fail with error NC_EFILTER if an attempt is made to add a filter to a variable whose type is variable-length. A variable is variable-length if it is of type string or VLEN or transitively (via a compound type) contains a string or VLEN. Also added a test case for this. ## Misc Changes 1. Turn off a number of debugging statements
1242 lines
30 KiB
C
1242 lines
30 KiB
C
/*
|
|
* Copyright 2018, University Corporation for Atmospheric Research
|
|
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#ifdef HAVE_FCNTL_H
|
|
#include <fcntl.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_DIRENT_H
|
|
#include <dirent.h>
|
|
#endif
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#include <io.h>
|
|
#include <wchar.h>
|
|
#include <locale.h>
|
|
#include <direct.h>
|
|
#endif
|
|
#ifdef __hpux
|
|
#include <locale.h>
|
|
#endif
|
|
|
|
#include "netcdf.h"
|
|
#include "ncpathmgr.h"
|
|
#include "nclog.h"
|
|
#include "nclist.h"
|
|
#include "ncbytes.h"
|
|
#include "ncuri.h"
|
|
#include "ncutf8.h"
|
|
|
|
#undef DEBUGPATH
|
|
static int pathdebug = -1;
|
|
#undef DEBUG
|
|
|
|
#ifdef DEBUG
|
|
#define REPORT(e,msg) report((e),(msg),__LINE__)
|
|
#else
|
|
#define REPORT(e,msg)
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#define access _access
|
|
#define mkdir _mkdir
|
|
#define rmdir _rmdir
|
|
#define getcwd _getcwd
|
|
#endif
|
|
|
|
/*
|
|
Code to provide some path conversion code so that
|
|
paths in one format can be used on a platform that uses
|
|
a different format.
|
|
See the documentation in ncpathmgr.h for details.
|
|
*/
|
|
|
|
/* Define legal windows drive letters */
|
|
static const char* windrive = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/";
|
|
|
|
static const char netdrive = '/';
|
|
|
|
static const size_t cdlen = 10; /* strlen("/cygdrive/") */
|
|
|
|
static int pathinitialized = 0;
|
|
|
|
static const char* cygwinspecial[] =
|
|
{"/bin/","/dev/","/etc/","/home/",
|
|
"/lib/","/proc/","/sbin/","/tmp/",
|
|
"/usr/","/var/",NULL};
|
|
|
|
static const struct Path {
|
|
int kind;
|
|
int drive;
|
|
char* path;
|
|
} empty = {NCPD_UNKNOWN,0,NULL};
|
|
|
|
/* Keep the working directory kind and drive */
|
|
static char wdprefix[8192];
|
|
|
|
/* Keep CYGWIN/MSYS2 mount point */
|
|
static struct MountPoint {
|
|
int defined;
|
|
char prefix[8192]; /*minus leading drive */
|
|
char drive;
|
|
} mountpoint;
|
|
|
|
/* Pick the target kind for testing */
|
|
static int testkind = 0;
|
|
|
|
static int parsepath(const char* inpath, struct Path* path);
|
|
static int unparsepath(struct Path* p, char** pathp, int target);
|
|
static int getwdpath(void);
|
|
static void clearPath(struct Path* path);
|
|
static void pathinit(void);
|
|
static int iscygwinspecial(const char* path);
|
|
static int testurl(const char* path);
|
|
|
|
#ifdef WINPATH
|
|
static int ansi2utf8(const char* local, char** u8p);
|
|
static int ansi2wide(const char* local, wchar_t** u16p);
|
|
static int utf82wide(const char* utf8, wchar_t** u16p);
|
|
static int wide2utf8(const wchar_t* u16, char** u8p);
|
|
#endif
|
|
|
|
/*Forward*/
|
|
#ifdef DEBUG
|
|
static void report(int stat, const char* msg, int line);
|
|
#endif
|
|
static char* printPATH(struct Path* p);
|
|
|
|
EXTERNL
|
|
char* /* caller frees */
|
|
NCpathcvt(const char* inpath)
|
|
{
|
|
int stat = NC_NOERR;
|
|
char* tmp1 = NULL;
|
|
char* result = NULL;
|
|
struct Path inparsed = empty;
|
|
int target = NCgetlocalpathkind();
|
|
|
|
if(inpath == NULL) goto done; /* defensive driving */
|
|
|
|
if(!pathinitialized) pathinit();
|
|
|
|
if(testurl(inpath)) { /* Pass thru URLs */
|
|
if((result = strdup(inpath))==NULL) stat = NC_ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
if((stat = parsepath(inpath,&inparsed)))
|
|
{REPORT(stat,"NCpathcvt: parsepath"); goto done;}
|
|
if(pathdebug > 0)
|
|
fprintf(stderr,">>> NCpathcvt: inparsed=%s\n",printPATH(&inparsed));
|
|
|
|
if((stat = unparsepath(&inparsed,&result,target)))
|
|
{REPORT(stat,"NCpathcvt: unparsepath"); goto done;}
|
|
|
|
done:
|
|
if(pathdebug > 0) {
|
|
fprintf(stderr,">>> inpath=|%s| result=|%s|\n",
|
|
inpath?inpath:"NULL",result?result:"NULL");
|
|
fflush(stderr);
|
|
}
|
|
if(stat) {
|
|
nullfree(result); result = NULL;
|
|
nclog(NCLOGERR,"NCpathcvt: stat=%d (%s)",
|
|
stat,nc_strerror(stat));
|
|
}
|
|
nullfree(tmp1);
|
|
clearPath(&inparsed);
|
|
return result;
|
|
}
|
|
|
|
EXTERNL
|
|
int
|
|
NCpathcanonical(const char* srcpath, char** canonp)
|
|
{
|
|
int stat = NC_NOERR;
|
|
char* canon = NULL;
|
|
struct Path path = empty;
|
|
|
|
if(srcpath == NULL) goto done;
|
|
|
|
if(!pathinitialized) pathinit();
|
|
|
|
/* parse the src path */
|
|
if((stat = parsepath(srcpath,&path))) {goto done;}
|
|
|
|
/* Convert to cygwin form */
|
|
if((stat = unparsepath(&path,&canon, NCPD_CYGWIN)))
|
|
goto done;
|
|
|
|
if(canonp) {*canonp = canon; canon = NULL;}
|
|
|
|
done:
|
|
nullfree(canon);
|
|
clearPath(&path);
|
|
return stat;
|
|
}
|
|
|
|
EXTERNL
|
|
char* /* caller frees */
|
|
NCpathabsolute(const char* relpath)
|
|
{
|
|
int stat = NC_NOERR;
|
|
struct Path canon = empty;
|
|
char* tmp1 = NULL;
|
|
char* result = NULL;
|
|
size_t len;
|
|
|
|
if(relpath == NULL) goto done; /* defensive driving */
|
|
|
|
if(!pathinitialized) pathinit();
|
|
|
|
/* Decompose path */
|
|
if((stat = parsepath(relpath,&canon)))
|
|
{REPORT(stat,"pathabs: parsepath"); goto done;}
|
|
|
|
/* See if relative */
|
|
if(canon.kind == NCPD_REL) {
|
|
/* prepend the wd path to the inpath, including drive letter, if any */
|
|
len = strlen(wdprefix)+strlen(canon.path)+1+1;
|
|
if((tmp1 = (char*)malloc(len))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
tmp1[0] = '\0';
|
|
strlcat(tmp1,wdprefix,len);
|
|
strlcat(tmp1,"/",len);
|
|
strlcat(tmp1,canon.path,len);
|
|
nullfree(canon.path);
|
|
canon.path = NULL;
|
|
/* Reparse */
|
|
result = NCpathabsolute(tmp1);
|
|
goto done;
|
|
}
|
|
/* rebuild */
|
|
if((stat=unparsepath(&canon,&result,NCgetlocalpathkind())))
|
|
{REPORT(stat,"pathabs: unparsepath"); goto done;}
|
|
done:
|
|
if(pathdebug > 0) {
|
|
fprintf(stderr,">>> relpath=|%s| result=|%s|\n",
|
|
relpath?relpath:"NULL",result?result:"NULL");
|
|
fflush(stderr);
|
|
}
|
|
if(stat) {
|
|
nullfree(tmp1); tmp1 = NULL;
|
|
nclog(NCLOGERR,"NCpathcvt: stat=%d (%s)",
|
|
stat,nc_strerror(stat));
|
|
}
|
|
clearPath(&canon);
|
|
nullfree(tmp1);
|
|
return result;
|
|
}
|
|
|
|
|
|
/* Testing support */
|
|
EXTERNL
|
|
char* /* caller frees */
|
|
NCpathcvt_test(const char* inpath, int ukind, int udrive)
|
|
{
|
|
char* result = NULL;
|
|
struct MountPoint old;
|
|
|
|
if(!pathinitialized) pathinit();
|
|
|
|
old = mountpoint;
|
|
memset(&mountpoint,0,sizeof(mountpoint));
|
|
|
|
mountpoint.drive = (char)udrive;
|
|
mountpoint.defined = (mountpoint.drive || nulllen(mountpoint.prefix) > 0);
|
|
testkind = ukind;
|
|
result = NCpathcvt(inpath);
|
|
mountpoint = old;
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
pathinit(void)
|
|
{
|
|
if(pathinitialized) return;
|
|
|
|
/* Check for path debug env vars */
|
|
if(pathdebug < 0) {
|
|
const char* s = getenv("NCPATHDEBUG");
|
|
pathdebug = (s == NULL ? 0 : 1);
|
|
}
|
|
(void)getwdpath();
|
|
memset(&mountpoint,0,sizeof(mountpoint));
|
|
#ifdef REGEDIT
|
|
{ /* See if we can get the MSYS2 prefix from the registry */
|
|
if(getmountpoint(mountpoint.prefix,sizeof(mountpoint.prefix)))
|
|
goto next;
|
|
mountpoint.defined = 1;
|
|
if(pathdebug > 0)
|
|
fprintf(stderr,">>>> registry: mountprefix=|%s|\n",mountpoint.prefix);
|
|
}
|
|
next:
|
|
#endif
|
|
if(!mountpoint.defined) {
|
|
mountpoint.prefix[0] = '\0';
|
|
/* See if MSYS2_PREFIX is defined */
|
|
if(getenv("MSYS2_PREFIX")) {
|
|
const char* m2 = getenv("MSYS2_PREFIX");
|
|
mountpoint.prefix[0] = '\0';
|
|
strlcat(mountpoint.prefix,m2,sizeof(mountpoint.prefix));
|
|
}
|
|
if(pathdebug > 0) {
|
|
fprintf(stderr,">>>> prefix: mountprefix=|%s|\n",mountpoint.prefix);
|
|
}
|
|
}
|
|
if(mountpoint.defined) {
|
|
char* p;
|
|
size_t size = strlen(mountpoint.prefix);
|
|
for(p=mountpoint.prefix;*p;p++) {if(*p == '\\') *p = '/';} /* forward slash*/
|
|
if(mountpoint.prefix[size-1] == '/') {
|
|
size--;
|
|
mountpoint.prefix[size] = '\0'; /* no trailing slash */
|
|
}
|
|
/* Finally extract the drive letter, if any */
|
|
/* assumes mount prefix is in windows form */
|
|
mountpoint.drive = 0;
|
|
if(strchr(windrive,mountpoint.prefix[0]) != NULL
|
|
&& mountpoint.prefix[1] == ':') {
|
|
char* q = mountpoint.prefix;
|
|
mountpoint.drive = mountpoint.prefix[0];
|
|
/* Shift prefix left 2 chars */
|
|
for(p=mountpoint.prefix+2;*p;p++) {*q++ = *p;}
|
|
*q = '\0';
|
|
}
|
|
}
|
|
pathinitialized = 1;
|
|
}
|
|
|
|
static void
|
|
clearPath(struct Path* path)
|
|
{
|
|
nullfree(path->path);
|
|
path->path = NULL;
|
|
}
|
|
|
|
/* Unfortunately, not all cygwin paths start with /cygdrive.
|
|
So see if the path starts with one of the special paths.
|
|
*/
|
|
static int
|
|
iscygwinspecial(const char* path)
|
|
{
|
|
const char** p;
|
|
if(path == NULL) return 0;
|
|
for(p=cygwinspecial;*p;p++) {
|
|
if(strncmp(*p,path,strlen(*p))==0) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* return 1 if path looks like a url; 0 otherwise */
|
|
static int
|
|
testurl(const char* path)
|
|
{
|
|
int isurl = 0;
|
|
NCURI* tmpurl = NULL;
|
|
|
|
if(path == NULL) return 0;
|
|
|
|
/* Ok, try to parse as a url */
|
|
ncuriparse(path,&tmpurl);
|
|
isurl = (tmpurl == NULL?0:1);
|
|
ncurifree(tmpurl);
|
|
return isurl;
|
|
}
|
|
|
|
#ifdef WINPATH
|
|
|
|
/*
|
|
Provide wrappers for Path-related functions
|
|
*/
|
|
|
|
EXTERNL
|
|
FILE*
|
|
NCfopen(const char* path, const char* flags)
|
|
{
|
|
int stat = NC_NOERR;
|
|
FILE* f = NULL;
|
|
char* bflags = NULL;
|
|
char* cvtpath = NULL;
|
|
wchar_t* wpath = NULL;
|
|
wchar_t* wflags = NULL;
|
|
size_t flaglen = strlen(flags)+1+1;
|
|
|
|
bflags = (char*)malloc(flaglen);
|
|
bflags[0] = '\0';
|
|
strlcat(bflags,flags,flaglen);
|
|
#ifdef _WIN32
|
|
strlcat(bflags,"b",flaglen);
|
|
#endif
|
|
cvtpath = NCpathcvt(path);
|
|
if(cvtpath == NULL) return NULL;
|
|
/* Convert from local to wide */
|
|
if((stat = utf82wide(cvtpath,&wpath)))
|
|
{REPORT(stat,"utf282wide"); goto done;}
|
|
if((stat = ansi2wide(bflags,&wflags)))
|
|
{REPORT(stat,"ansi2wide"); goto done;}
|
|
f = _wfopen(wpath,wflags);
|
|
done:
|
|
nullfree(cvtpath);
|
|
nullfree(wpath);
|
|
nullfree(wflags);
|
|
nullfree(bflags);
|
|
return f;
|
|
}
|
|
|
|
EXTERNL
|
|
int
|
|
NCopen3(const char* path, int flags, int mode)
|
|
{
|
|
int stat = NC_NOERR;
|
|
int fd = -1;
|
|
char* cvtpath = NULL;
|
|
wchar_t* wpath = NULL;
|
|
cvtpath = NCpathcvt(path);
|
|
if(cvtpath == NULL) goto done;
|
|
/* Convert from utf8 to wide */
|
|
if((stat = utf82wide(cvtpath,&wpath))) goto done;
|
|
#ifdef _WIN32
|
|
flags |= O_BINARY;
|
|
#endif
|
|
fd = _wopen(wpath,flags,mode);
|
|
done:
|
|
nullfree(cvtpath);
|
|
nullfree(wpath);
|
|
return fd;
|
|
}
|
|
|
|
EXTERNL
|
|
int
|
|
NCopen2(const char *path, int flags)
|
|
{
|
|
return NCopen3(path,flags,0);
|
|
}
|
|
|
|
#ifdef HAVE_DIRENT_H
|
|
EXTERNL
|
|
DIR*
|
|
NCopendir(const char* path)
|
|
{
|
|
DIR* ent = NULL;
|
|
char* cvtname = NCpathcvt(path);
|
|
if(cvtname == NULL) return NULL;
|
|
ent = opendir(cvtname);
|
|
free(cvtname);
|
|
return ent;
|
|
}
|
|
|
|
EXTERNL
|
|
int
|
|
NCclosedir(DIR* ent)
|
|
{
|
|
int stat = NC_NOERR;
|
|
if(closedir(ent) < 0) stat = errno;
|
|
return stat;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Provide wrappers for other file system functions
|
|
*/
|
|
|
|
/* Return access applied to path+mode */
|
|
EXTERNL
|
|
int
|
|
NCaccess(const char* path, int mode)
|
|
{
|
|
int status = 0;
|
|
char* cvtpath = NULL;
|
|
wchar_t* wpath = NULL;
|
|
if((cvtpath = NCpathcvt(path)) == NULL)
|
|
{status = EINVAL; goto done;}
|
|
if((status = utf82wide(cvtpath,&wpath))) {status = ENOENT; goto done;}
|
|
if(_waccess(wpath,mode) < 0) {status = errno; goto done;}
|
|
done:
|
|
free(cvtpath);
|
|
free(wpath);
|
|
errno = status;
|
|
return (errno?-1:0);
|
|
}
|
|
|
|
EXTERNL
|
|
int
|
|
NCremove(const char* path)
|
|
{
|
|
int status = 0;
|
|
char* cvtpath = NULL;
|
|
wchar_t* wpath = NULL;
|
|
if((cvtpath = NCpathcvt(path)) == NULL) {status=ENOMEM; goto done;}
|
|
if((status = utf82wide(cvtpath,&wpath))) {status = ENOENT; goto done;}
|
|
if(_wremove(wpath) < 0) {status = errno; goto done;}
|
|
done:
|
|
free(cvtpath);
|
|
free(wpath);
|
|
errno = status;
|
|
return (errno?-1:0);
|
|
}
|
|
|
|
EXTERNL
|
|
int
|
|
NCmkdir(const char* path, int mode)
|
|
{
|
|
int status = 0;
|
|
char* cvtpath = NULL;
|
|
wchar_t* wpath = NULL;
|
|
if((cvtpath = NCpathcvt(path)) == NULL) {status=ENOMEM; goto done;}
|
|
if((status = utf82wide(cvtpath,&wpath))) {status = ENOENT; goto done;}
|
|
if(_wmkdir(wpath) < 0) {status = errno; goto done;}
|
|
done:
|
|
free(cvtpath);
|
|
free(wpath);
|
|
errno = status;
|
|
return (errno?-1:0);
|
|
}
|
|
|
|
EXTERNL
|
|
int
|
|
NCrmdir(const char* path)
|
|
{
|
|
int status = 0;
|
|
char* cvtname = NCpathcvt(path);
|
|
if(cvtname == NULL) {errno = ENOENT; return -1;}
|
|
status = rmdir(cvtname);
|
|
free(cvtname);
|
|
return status;
|
|
}
|
|
|
|
EXTERNL
|
|
char*
|
|
NCgetcwd(char* cwdbuf, size_t cwdlen)
|
|
{
|
|
int status = NC_NOERR;
|
|
char* path = NULL;
|
|
size_t len;
|
|
struct Path wd;
|
|
|
|
errno = 0;
|
|
if(cwdlen == 0) {status = ENAMETOOLONG; goto done;}
|
|
if(!pathinitialized) pathinit();
|
|
if((status = getwdpath())) {status = ENOENT; goto done;}
|
|
if((status = parsepath(wdprefix,&wd))) {status = EINVAL; goto done;}
|
|
if((status = unparsepath(&wd,&path,NCgetlocalpathkind()))) {status = EINVAL; goto done;}
|
|
len = strlen(path);
|
|
if(len >= cwdlen) {status = ENAMETOOLONG; goto done;}
|
|
if(cwdbuf == NULL) {
|
|
if((cwdbuf = malloc(cwdlen))==NULL)
|
|
{status = NCTHROW(ENOMEM); goto done;}
|
|
}
|
|
memcpy(cwdbuf,path,len+1);
|
|
done:
|
|
clearPath(&wd);
|
|
nullfree(path);
|
|
errno = status;
|
|
return cwdbuf;
|
|
}
|
|
|
|
EXTERNL
|
|
int
|
|
NCmkstemp(char* base)
|
|
{
|
|
int stat = 0;
|
|
int fd, rno;
|
|
char* tmp = NULL;
|
|
size_t len;
|
|
char* xp = NULL;
|
|
char* cvtpath = NULL;
|
|
int attempts;
|
|
|
|
cvtpath = NCpathcvt(base);
|
|
len = strlen(cvtpath);
|
|
xp = cvtpath+(len-6);
|
|
assert(memcmp(xp,"XXXXXX",6)==0);
|
|
for(attempts=10;attempts>0;attempts--) {
|
|
/* The Windows version of mkstemp does not work right;
|
|
it only allows for 26 possible XXXXXX values */
|
|
/* Need to simulate by using some kind of pseudo-random number */
|
|
rno = rand();
|
|
if(rno < 0) rno = -rno;
|
|
snprintf(xp,7,"%06d",rno);
|
|
fd=NCopen3(cvtpath,O_RDWR|O_BINARY|O_CREAT, _S_IREAD|_S_IWRITE);
|
|
if(fd >= 0) break;
|
|
}
|
|
if(fd < 0) {
|
|
nclog(NCLOGERR, "Could not create temp file: %s",tmp);
|
|
stat = EACCES;
|
|
goto done;
|
|
}
|
|
done:
|
|
nullfree(cvtpath);
|
|
if(stat && fd >= 0) {close(fd);}
|
|
return (stat?-1:fd);
|
|
}
|
|
|
|
#ifdef HAVE_SYS_STAT_H
|
|
EXTERNL
|
|
int
|
|
NCstat(const char* path, struct stat* buf)
|
|
{
|
|
int status = 0;
|
|
char* cvtpath = NULL;
|
|
wchar_t* wpath = NULL;
|
|
if((cvtpath = NCpathcvt(path)) == NULL) {status=ENOMEM; goto done;}
|
|
if((status = utf82wide(cvtpath,&wpath))) {status = ENOENT; goto done;}
|
|
if(_wstat64(wpath,buf) < 0) {status = errno; goto done;}
|
|
done:
|
|
free(cvtpath);
|
|
free(wpath);
|
|
errno = status;
|
|
return (errno?-1:0);
|
|
}
|
|
#endif /*HAVE_SYS_STAT_H*/
|
|
|
|
int
|
|
NCpath2utf8(const char* s, char** u8p)
|
|
{
|
|
return ansi2utf8(s,u8p);
|
|
}
|
|
|
|
#else /*!WINPATH*/
|
|
|
|
int
|
|
NCpath2utf8(const char* path, char** u8p)
|
|
{
|
|
int stat = NC_NOERR;
|
|
char* u8 = NULL;
|
|
if(path != NULL) {
|
|
u8 = strdup(path);
|
|
if(u8 == NULL) {stat = NC_ENOMEM; goto done;}
|
|
}
|
|
if(u8p) {*u8p = u8; u8 = NULL;}
|
|
done:
|
|
return stat;
|
|
}
|
|
#endif /*!WINPATH*/
|
|
|
|
EXTERNL int
|
|
NChasdriveletter(const char* path)
|
|
{
|
|
int stat = NC_NOERR;
|
|
int hasdl = 0;
|
|
struct Path canon = empty;
|
|
|
|
if(!pathinitialized) pathinit();
|
|
|
|
if((stat = parsepath(path,&canon))) goto done;
|
|
hasdl = (canon.drive != 0);
|
|
done:
|
|
clearPath(&canon);
|
|
return hasdl;
|
|
}
|
|
|
|
EXTERNL int
|
|
NCisnetworkpath(const char* path)
|
|
{
|
|
int stat = NC_NOERR;
|
|
int isnp = 0;
|
|
struct Path canon = empty;
|
|
|
|
if(!pathinitialized) pathinit();
|
|
|
|
if((stat = parsepath(path,&canon))) goto done;
|
|
isnp = (canon.drive == netdrive);
|
|
done:
|
|
clearPath(&canon);
|
|
return isnp;
|
|
}
|
|
|
|
/**************************************************/
|
|
/* Utilities */
|
|
|
|
/* Parse a path */
|
|
static int
|
|
parsepath(const char* inpath, struct Path* path)
|
|
{
|
|
int stat = NC_NOERR;
|
|
char* tmp1 = NULL;
|
|
size_t len;
|
|
char* p;
|
|
|
|
assert(path);
|
|
memset(path,0,sizeof(struct Path));
|
|
|
|
if(inpath == NULL) goto done; /* defensive driving */
|
|
|
|
#if 0
|
|
/* Convert to UTF8 */
|
|
if((stat = NCpath2utf8(inpath,&tmp1))) goto done;
|
|
#else
|
|
tmp1 = strdup(inpath);
|
|
#endif
|
|
/* Convert to forward slash to simplify later code */
|
|
for(p=tmp1;*p;p++) {if(*p == '\\') *p = '/';}
|
|
|
|
/* parse all paths to 2 parts:
|
|
1. drive letter (optional)
|
|
2. path after drive letter
|
|
*/
|
|
|
|
len = strlen(tmp1);
|
|
|
|
/* 1. look for Windows network path //...; drive letter is faked using
|
|
the character '/' */
|
|
if(len >= 2 && (tmp1[0] == '/') && (tmp1[1] == '/')) {
|
|
path->drive = netdrive;
|
|
/* Remainder */
|
|
if(tmp1[2] == '\0')
|
|
path->path = NULL;
|
|
else
|
|
path->path = strdup(tmp1+1); /*keep first '/' */
|
|
if(path == NULL)
|
|
{stat = NC_ENOMEM; goto done;}
|
|
path->kind = NCPD_WIN;
|
|
}
|
|
/* 2. Look for leading /cygdrive/D where D is a single-char drive letter */
|
|
else if(len >= (cdlen+1)
|
|
&& memcmp(tmp1,"/cygdrive/",cdlen)==0
|
|
&& strchr(windrive,tmp1[cdlen]) != NULL
|
|
&& (tmp1[cdlen+1] == '/'
|
|
|| tmp1[cdlen+1] == '\0')) {
|
|
/* Assume this is a cygwin path */
|
|
path->drive = tmp1[cdlen];
|
|
/* Remainder */
|
|
if(tmp1[cdlen+1] == '\0')
|
|
path->path = NULL;
|
|
else
|
|
path->path = strdup(tmp1+cdlen+1);
|
|
if(path == NULL)
|
|
{stat = NC_ENOMEM; goto done;}
|
|
path->kind = NCPD_CYGWIN;
|
|
}
|
|
/* 4. Look for windows path: D:/... where D is a single-char
|
|
drive letter */
|
|
else if(len >= 2
|
|
&& strchr(windrive,tmp1[0]) != NULL
|
|
&& tmp1[1] == ':'
|
|
&& (tmp1[2] == '\0' || tmp1[2] == '/')) {
|
|
/* Assume this is a windows path */
|
|
path->drive = tmp1[0];
|
|
/* Remainder */
|
|
if(tmp1[2] == '\0')
|
|
path->path = NULL;
|
|
else
|
|
path->path = strdup(tmp1+2);
|
|
if(path == NULL)
|
|
{stat = NC_ENOMEM; goto done;}
|
|
path->kind = NCPD_WIN; /* Might be MINGW */
|
|
}
|
|
#if 0
|
|
/* X. look for MSYS2 path /D/... */
|
|
else if(len >= 2
|
|
&& (tmp1[0] == '/')
|
|
&& strchr(windrive,tmp1[1]) != NULL
|
|
&& (tmp1[2] == '/' || tmp1[2] == '\0')) {
|
|
/* Assume this is an MSYS2 path */
|
|
path->drive = tmp1[1];
|
|
/* Remainder */
|
|
if(tmp1[2] == '\0')
|
|
path->path = NULL;
|
|
else
|
|
path->path = strdup(tmp1+2);
|
|
if(path == NULL)
|
|
{stat = NC_ENOMEM; goto done;}
|
|
path->kind = NCPD_MSYS;
|
|
}
|
|
#endif
|
|
/* 5. look for *nix* path; note this includes MSYS2 paths as well */
|
|
else if(len >= 1 && tmp1[0] == '/') {
|
|
/* Assume this is a *nix path */
|
|
path->drive = 0; /* no drive letter */
|
|
/* Remainder */
|
|
path->path = tmp1; tmp1 = NULL;
|
|
path->kind = NCPD_NIX;
|
|
} else {/* 6. Relative path of unknown type */
|
|
path->kind = NCPD_REL;
|
|
path->path = tmp1; tmp1 = NULL;
|
|
}
|
|
|
|
done:
|
|
nullfree(tmp1);
|
|
if(stat) {clearPath(path);}
|
|
return stat;
|
|
}
|
|
|
|
static int
|
|
unparsepath(struct Path* xp, char** pathp, int target)
|
|
{
|
|
int stat = NC_NOERR;
|
|
size_t len;
|
|
char* path = NULL;
|
|
char sdrive[4] = "\0\0\0\0";
|
|
char* p = NULL;
|
|
int cygspecial = 0;
|
|
int drive = 0;
|
|
|
|
/* Short circuit a relative path */
|
|
if(xp->kind == NCPD_REL) {
|
|
/* Pass thru relative paths, but with proper slashes */
|
|
if((path = strdup(xp->path))==NULL) stat = NC_ENOMEM;
|
|
if(target == NCPD_WIN || target == NCPD_MSYS) {
|
|
char* p;
|
|
for(p=path;*p;p++) {if(*p == '/') *p = '\\';} /* back slash*/
|
|
}
|
|
goto exit;
|
|
}
|
|
|
|
/* We need a two level switch with an arm
|
|
for every pair of (xp->kind,target)
|
|
*/
|
|
|
|
#define CASE(k,t) case ((k)*10+(t))
|
|
|
|
switch (xp->kind*10 + target) {
|
|
CASE(NCPD_NIX,NCPD_NIX):
|
|
assert(xp->drive == 0);
|
|
len = nulllen(xp->path)+1;
|
|
if((path = (char*)malloc(len))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
path[0] = '\0';
|
|
if(xp->path != NULL)
|
|
strlcat(path,xp->path,len);
|
|
break;
|
|
CASE(NCPD_NIX,NCPD_MSYS):
|
|
CASE(NCPD_NIX,NCPD_WIN):
|
|
assert(xp->drive == 0);
|
|
len = nulllen(xp->path)+1;
|
|
if(!mountpoint.defined)
|
|
{stat = NC_EINVAL; goto done;} /* drive required */
|
|
len += (strlen(mountpoint.prefix) + 2);
|
|
if((path = (char*)malloc(len))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
path[0] = '\0';
|
|
assert(mountpoint.drive != 0);
|
|
sdrive[0] = mountpoint.drive;
|
|
sdrive[1] = ':';
|
|
sdrive[2] = '\0';
|
|
strlcat(path,sdrive,len);
|
|
strlcat(path,mountpoint.prefix,len);
|
|
if(xp->path != NULL) strlcat(path,xp->path,len);
|
|
for(p=path;*p;p++) {if(*p == '/') *p = '\\';} /* restore back slash */
|
|
break;
|
|
CASE(NCPD_NIX,NCPD_CYGWIN):
|
|
assert(xp->drive == 0);
|
|
/* Is this one of the special cygwin paths? */
|
|
cygspecial = iscygwinspecial(xp->path);
|
|
len = 0;
|
|
if(!cygspecial && mountpoint.drive != 0) {
|
|
len = cdlen + 1+1; /* /cygdrive/D */
|
|
len += nulllen(mountpoint.prefix);
|
|
}
|
|
if(xp->path)
|
|
len += strlen(xp->path);
|
|
len++; /* nul term */
|
|
if((path = (char*)malloc(len))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
path[0] = '\0';
|
|
if(!cygspecial && mountpoint.drive != 0) {
|
|
strlcat(path,"/cygdrive/",len);
|
|
sdrive[0] = mountpoint.drive;
|
|
sdrive[1] = '\0';
|
|
strlcat(path,sdrive,len);
|
|
strlcat(path,mountpoint.prefix,len);
|
|
}
|
|
if(xp->path)
|
|
strlcat(path,xp->path,len);
|
|
break;
|
|
|
|
CASE(NCPD_CYGWIN,NCPD_NIX):
|
|
len = nulllen(xp->path);
|
|
if(xp->drive != 0)
|
|
len += (cdlen + 2); /* /cygdrive/D */
|
|
len++;
|
|
if((path = (char*)malloc(len))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
path[0] = '\0';
|
|
if(xp->drive != 0) {
|
|
strlcat(path,"/cygdrive/",len);
|
|
sdrive[0] = xp->drive; sdrive[1] = '\0';
|
|
strlcat(path,sdrive,len);
|
|
}
|
|
if(xp->path)
|
|
strlcat(path,xp->path,len);
|
|
break;
|
|
|
|
CASE(NCPD_CYGWIN,NCPD_WIN):
|
|
CASE(NCPD_CYGWIN,NCPD_MSYS):
|
|
len = nulllen(xp->path)+1;
|
|
if(xp->drive == 0 && !mountpoint.defined)
|
|
{stat = NC_EINVAL; goto done;} /* drive required */
|
|
if (xp->drive == 0)
|
|
len += (strlen(mountpoint.prefix) + 2);
|
|
else
|
|
len += sizeof(sdrive);
|
|
len++;
|
|
if((path = (char*)malloc(len))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
path[0] = '\0';
|
|
if(xp->drive != 0)
|
|
drive = xp->drive;
|
|
else
|
|
drive = mountpoint.drive;
|
|
sdrive[0] = drive; sdrive[1] = ':'; sdrive[2] = '\0';
|
|
strlcat(path,sdrive,len);
|
|
if(xp->path != NULL)
|
|
strlcat(path,xp->path,len);
|
|
for(p=path;*p;p++) {if(*p == '/') *p = '\\';} /* restore back slash */
|
|
break;
|
|
|
|
CASE(NCPD_CYGWIN,NCPD_CYGWIN):
|
|
len = nulllen(xp->path)+1;
|
|
if(xp->drive != 0)
|
|
len += (cdlen + 2);
|
|
len++;
|
|
if((path = (char*)malloc(len))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
path[0] = '\0';
|
|
if(xp->drive != 0) {
|
|
sdrive[0] = xp->drive; sdrive[1] = '\0';
|
|
strlcat(path,"/cygdrive/",len);
|
|
strlcat(path,sdrive,len);
|
|
}
|
|
if(xp->path != NULL)
|
|
strlcat(path,xp->path,len);
|
|
break;
|
|
|
|
CASE(NCPD_WIN, NCPD_WIN) :
|
|
CASE(NCPD_MSYS, NCPD_MSYS) :
|
|
CASE(NCPD_WIN, NCPD_MSYS) :
|
|
CASE(NCPD_MSYS, NCPD_WIN) :
|
|
if(xp->drive == 0 && !mountpoint.defined)
|
|
{stat = NC_EINVAL; goto done;} /* drive required */
|
|
len = nulllen(xp->path) + 1 + sizeof(sdrive);
|
|
if(xp->drive == 0)
|
|
len += strlen(mountpoint.prefix);
|
|
len++;
|
|
if((path = (char*)malloc(len))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
path[0] = '\0';
|
|
if(xp->drive != 0)
|
|
drive = xp->drive;
|
|
else
|
|
drive = mountpoint.drive;
|
|
sdrive[0] = drive;
|
|
sdrive[1] = (drive == netdrive ? '\0' : ':');
|
|
sdrive[2] = '\0';
|
|
strlcat(path,sdrive,len);
|
|
if(xp->path != NULL)
|
|
strlcat(path,xp->path,len);
|
|
for(p=path;*p;p++) {if(*p == '/') *p = '\\';} /* restore back slash */
|
|
break;
|
|
|
|
CASE(NCPD_WIN,NCPD_NIX):
|
|
CASE(NCPD_MSYS,NCPD_NIX):
|
|
assert(xp->drive != 0);
|
|
len = nulllen(xp->path)+1;
|
|
if(xp->drive != 0)
|
|
len += sizeof(sdrive);
|
|
len++;
|
|
if((path = (char*)malloc(len))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
path[0] = '\0';
|
|
if(xp->drive != 0) {
|
|
sdrive[0] = '/'; sdrive[1] = xp->drive; sdrive[2] = '\0';
|
|
strlcat(path,sdrive,len);
|
|
}
|
|
if(xp->path != NULL) strlcat(path,xp->path,len);
|
|
break;
|
|
|
|
CASE(NCPD_MSYS,NCPD_CYGWIN):
|
|
CASE(NCPD_WIN,NCPD_CYGWIN):
|
|
assert(xp->drive != 0);
|
|
len = nulllen(xp->path)+1;
|
|
len += (cdlen + 2);
|
|
len++;
|
|
if((path = (char*)malloc(len))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
path[0] = '\0';
|
|
sdrive[0] = xp->drive; sdrive[1] = '\0';
|
|
strlcat(path,"/cygdrive/",len);
|
|
strlcat(path,sdrive,len);
|
|
if(xp->path != NULL) strlcat(path,xp->path,len);
|
|
break;
|
|
|
|
default: stat = NC_EINTERNAL; goto done;
|
|
}
|
|
|
|
if(pathdebug > 0)
|
|
fprintf(stderr,">>> unparse: target=%s xp=%s path=|%s|\n",NCgetkindname(target),printPATH(xp),path);
|
|
|
|
exit:
|
|
if(pathp) {*pathp = path; path = NULL;}
|
|
done:
|
|
nullfree(path);
|
|
return stat;
|
|
}
|
|
|
|
static int
|
|
getwdpath(void)
|
|
{
|
|
int stat = NC_NOERR;
|
|
char* path = NULL;
|
|
|
|
wdprefix[0] = '\0';
|
|
#ifdef _WIN32
|
|
{
|
|
wchar_t* wcwd = NULL;
|
|
wchar_t* wpath = NULL;
|
|
wcwd = (wchar_t*)calloc(8192, sizeof(wchar_t));
|
|
wpath = _wgetcwd(wcwd, 8192);
|
|
path = NULL;
|
|
stat = wide2utf8(wpath, &path);
|
|
free(wcwd);
|
|
if (stat) return stat;
|
|
strlcat(wdprefix,path,sizeof(wdprefix));
|
|
}
|
|
#else
|
|
{
|
|
getcwd(wdprefix, sizeof(wdprefix));
|
|
}
|
|
#endif
|
|
nullfree(path); path = NULL;
|
|
return stat;
|
|
}
|
|
|
|
int
|
|
NCgetinputpathkind(const char* inpath)
|
|
{
|
|
struct Path p;
|
|
int result = NCPD_UNKNOWN;
|
|
|
|
memset(&p,0,sizeof(p));
|
|
if(inpath == NULL) goto done; /* defensive driving */
|
|
if(testurl(inpath)) goto done;
|
|
if(!pathinitialized) pathinit();
|
|
if(parsepath(inpath,&p)) goto done;
|
|
|
|
done:
|
|
result = p.kind;
|
|
clearPath(&p);
|
|
return result;
|
|
}
|
|
|
|
int
|
|
NCgetlocalpathkind(void)
|
|
{
|
|
int kind = NCPD_UNKNOWN;
|
|
if(testkind) return testkind;
|
|
#ifdef __CYGWIN__
|
|
kind = NCPD_CYGWIN;
|
|
#elif defined _MSC_VER /* not _WIN32 */
|
|
kind = NCPD_WIN;
|
|
#elif defined __MSYS__
|
|
kind = NCPD_MSYS;
|
|
#elif defined __MINGW32__
|
|
kind = NCPD_WIN; /* alias */
|
|
#else
|
|
kind = NCPD_NIX;
|
|
#endif
|
|
return kind;
|
|
}
|
|
|
|
const char*
|
|
NCgetkindname(int kind)
|
|
{
|
|
switch (kind) {
|
|
case NCPD_UNKNOWN: return "NCPD_UNKNOWN";
|
|
case NCPD_NIX: return "NCPD_NIX";
|
|
case NCPD_MSYS: return "NCPD_MSYS";
|
|
case NCPD_CYGWIN: return "NCPD_CYGWIN";
|
|
case NCPD_WIN: return "NCPD_WIN";
|
|
/* same as WIN case NCPD_MINGW: return "NCPD_MINGW";*/
|
|
case NCPD_REL: return "NCPD_REL";
|
|
default: break;
|
|
}
|
|
return "NCPD_UNDEF";
|
|
}
|
|
|
|
#ifdef WINPATH
|
|
/**
|
|
* Converts the filename from Locale character set (presumably some
|
|
* ANSI character set like ISO-Latin-1 or UTF-8 to UTF-8
|
|
* @param local Pointer to a nul-terminated string in locale char set.
|
|
* @param u8p Pointer for returning the output utf8 string
|
|
*
|
|
* @return NC_NOERR return converted filename
|
|
* @return NC_EINVAL if conversion fails
|
|
* @return NC_ENOMEM if no memory available
|
|
*
|
|
*/
|
|
static int
|
|
ansi2utf8(const char* local, char** u8p)
|
|
{
|
|
int stat=NC_NOERR;
|
|
char* u8 = NULL;
|
|
int n;
|
|
wchar_t* u16 = NULL;
|
|
|
|
{
|
|
/* Get length of the converted string */
|
|
n = MultiByteToWideChar(CP_ACP, 0, local, -1, NULL, 0);
|
|
if (!n) {stat = NC_EINVAL; goto done;}
|
|
if((u16 = malloc(sizeof(wchar_t) * n))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
/* do the conversion */
|
|
if (!MultiByteToWideChar(CP_ACP, 0, local, -1, u16, n))
|
|
{stat = NC_EINVAL; goto done;}
|
|
/* Now reverse the process to produce utf8 */
|
|
n = WideCharToMultiByte(CP_UTF8, 0, u16, -1, NULL, 0, NULL, NULL);
|
|
if (!n) {stat = NC_EINVAL; goto done;}
|
|
if((u8 = malloc(sizeof(char) * n))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
if (!WideCharToMultiByte(CP_UTF8, 0, u16, -1, u8, n, NULL, NULL))
|
|
{stat = NC_EINVAL; goto done;}
|
|
}
|
|
if(u8p) {*u8p = u8; u8 = NULL;}
|
|
done:
|
|
nullfree(u8);
|
|
nullfree(u16);
|
|
return stat;
|
|
}
|
|
|
|
static int
|
|
ansi2wide(const char* local, wchar_t** u16p)
|
|
{
|
|
int stat=NC_NOERR;
|
|
wchar_t* u16 = NULL;
|
|
int n;
|
|
|
|
/* Get length of the converted string */
|
|
n = MultiByteToWideChar(CP_ACP, 0, local, -1, NULL, 0);
|
|
if (!n) {stat = NC_EINVAL; goto done;}
|
|
if((u16 = malloc(sizeof(wchar_t) * n))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
/* do the conversion */
|
|
if (!MultiByteToWideChar(CP_ACP, 0, local, -1, u16, n))
|
|
{stat = NC_EINVAL; goto done;}
|
|
if(u16p) {*u16p = u16; u16 = NULL;}
|
|
done:
|
|
nullfree(u16);
|
|
return stat;
|
|
}
|
|
|
|
static int
|
|
utf82wide(const char* utf8, wchar_t** u16p)
|
|
{
|
|
int stat=NC_NOERR;
|
|
wchar_t* u16 = NULL;
|
|
int n;
|
|
|
|
/* Get length of the converted string */
|
|
n = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
|
|
if (!n) {stat = NC_EINVAL; goto done;}
|
|
if((u16 = malloc(sizeof(wchar_t) * n))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
/* do the conversion */
|
|
if (!MultiByteToWideChar(CP_UTF8, 0, utf8, -1, u16, n))
|
|
{stat = NC_EINVAL; goto done;}
|
|
if(u16p) {*u16p = u16; u16 = NULL;}
|
|
done:
|
|
nullfree(u16);
|
|
return stat;
|
|
}
|
|
|
|
static int
|
|
wide2utf8(const wchar_t* u16, char** u8p)
|
|
{
|
|
int stat=NC_NOERR;
|
|
char* u8 = NULL;
|
|
int n;
|
|
|
|
/* Get length of the converted string */
|
|
n = WideCharToMultiByte(CP_UTF8, 0, u16, -1, NULL, 0, NULL, NULL);
|
|
if (!n) {stat = NC_EINVAL; goto done;}
|
|
if((u8 = malloc(sizeof(char) * n))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
/* do the conversion */
|
|
if (!WideCharToMultiByte(CP_UTF8, 0, u16, -1, u8, n, NULL, NULL))
|
|
{stat = NC_EINVAL; goto done;}
|
|
if(u8p) {*u8p = u8; u8 = NULL;}
|
|
done:
|
|
nullfree(u8);
|
|
return stat;
|
|
}
|
|
|
|
/* Set locale */
|
|
void
|
|
nc_setlocale_utf8(void)
|
|
{
|
|
#ifdef __hpux
|
|
setlocale(LC_CTYPE,"");
|
|
#else
|
|
setlocale(LC_ALL,"C.UTF8");
|
|
#endif
|
|
}
|
|
|
|
#endif /*WINPATH*/
|
|
|
|
static char*
|
|
printPATH(struct Path* p)
|
|
{
|
|
static char buf[4096];
|
|
buf[0] = '\0';
|
|
snprintf(buf,sizeof(buf),"Path{kind=%d drive='%c' path=|%s|}",
|
|
p->kind,(p->drive > 0?p->drive:'0'),p->path);
|
|
return buf;
|
|
}
|
|
|
|
#if 0
|
|
static int
|
|
hexfor(int c)
|
|
{
|
|
if(c >= '0' && c <= '9') return c - '0';
|
|
if(c >= 'a' && c <= 'f') return (c - 'a')+10;
|
|
if(c >= 'A' && c <= 'F') return (c - 'A')+10;
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
static char hexdigit[] = "0123456789abcdef";
|
|
|
|
EXTERNL void
|
|
printutf8hex(const char* s, char* sx)
|
|
{
|
|
const char* p;
|
|
char* q;
|
|
for(q=sx,p=s;*p;p++) {
|
|
unsigned int c = (unsigned char)*p;
|
|
if(c >= ' ' && c <= 127)
|
|
*q++ = (char)c;
|
|
else {
|
|
*q++ = '\\';
|
|
*q++ = 'x';
|
|
*q++ = hexdigit[(c>>4)&0xf];
|
|
*q++ = hexdigit[(c)&0xf];
|
|
}
|
|
}
|
|
*q = '\0';
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
static void
|
|
report(int stat, const char* msg, int line)
|
|
{
|
|
if(stat) {
|
|
nclog(NCLOGERR,"NCpathcvt(%d): %s: stat=%d (%s)",
|
|
line,msg,stat,nc_strerror(stat));
|
|
}
|
|
}
|
|
#endif
|