netcdf-c/libdispatch/dpathmgr.c
Dennis Heimbigner 9b7202bf06 Explicitly disallow variable length type compression
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
2022-02-19 16:47:31 -07:00

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