mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-11-21 03:13:42 +08:00
1392 lines
33 KiB
C
1392 lines
33 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 <direct.h>
|
|
#endif
|
|
#include <locale.h>
|
|
|
|
#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
|
|
|
|
#if WINVERMAJOR > 10 || (WINVERMAJOR == 10 && WINVERBUILD >= 17134)
|
|
/* Should be possible to use UTF8 directly */
|
|
#define WINUTF8
|
|
#else
|
|
#undef WINUTF8
|
|
#endif
|
|
|
|
#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;
|
|
char drive;
|
|
char* path;
|
|
} empty = {NCPD_UNKNOWN,0,NULL};
|
|
|
|
/* Keep the working directory kind and drive */
|
|
static char wdprefix[8192];
|
|
|
|
/* The current codepage */
|
|
#ifdef _WIN32
|
|
static int acp = -1;
|
|
#endif
|
|
|
|
/* Keep CYGWIN/MSYS2 mount point */
|
|
static struct MountPoint {
|
|
int defined;
|
|
char prefix[8192]; /*minus leading drive */
|
|
char drive;
|
|
} mountpoint;
|
|
|
|
/* Pick the platform kind for testing */
|
|
static int platform = 0;
|
|
|
|
static int parsepath(const char* inpath, struct Path* path);
|
|
static int unparsepath(struct Path* p, char** pathp, int platform);
|
|
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 platform= 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,platform)))
|
|
{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);
|
|
//fprintf(stderr,">>> ncpathcvt: inpath=%s result=%s\n",inpath,result);
|
|
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);
|
|
platform = ukind;
|
|
result = NCpathcvt(inpath);
|
|
mountpoint = old;
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
pathinit(void)
|
|
{
|
|
if(pathinitialized) return;
|
|
pathinitialized = 1; /* avoid recursion */
|
|
|
|
/* Check for path debug env vars */
|
|
if(pathdebug < 0) {
|
|
const char* s = getenv("NCPATHDEBUG");
|
|
pathdebug = (s == NULL ? 0 : 1);
|
|
}
|
|
(void)getwdpath();
|
|
|
|
#ifdef _WIN32
|
|
/* Get the current code page */
|
|
acp = GetACP();
|
|
#endif
|
|
|
|
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[64];
|
|
char* path8 = NULL; /* ACP -> UTF=8 */
|
|
char* bflags8 = NULL;
|
|
char* cvtpath = NULL;
|
|
wchar_t* wpath = NULL;
|
|
wchar_t* wflags = NULL;
|
|
size_t flaglen = strlen(flags)+1+1;
|
|
|
|
bflags[0] = '\0';
|
|
strlcat(bflags, flags, sizeof(bflags));
|
|
#ifdef _WIN32
|
|
strlcat(bflags,"b",sizeof(bflags));
|
|
#endif
|
|
|
|
/* First, convert from current Code Page to utf8 */
|
|
if((stat = ansi2utf8(path,&path8))) goto done;
|
|
if((stat = ansi2utf8(bflags,&bflags8))) goto done;
|
|
|
|
/* Localize */
|
|
if((cvtpath = NCpathcvt(path8))==NULL) goto done;
|
|
|
|
#ifdef WINUTF8
|
|
if(acp == CP_UTF8) {
|
|
/* This should take utf8 directly */
|
|
f = fopen(cvtpath,bflags8);
|
|
} else
|
|
#endif
|
|
{
|
|
/* Convert from utf8 to wide */
|
|
if((stat = utf82wide(cvtpath,&wpath))) goto done;
|
|
if((stat = utf82wide(bflags8,&wflags))) goto done;
|
|
f = _wfopen(wpath,wflags);
|
|
}
|
|
done:
|
|
nullfree(cvtpath);
|
|
nullfree(path8);
|
|
nullfree(wpath);
|
|
nullfree(wflags);
|
|
return f;
|
|
}
|
|
|
|
EXTERNL
|
|
int
|
|
NCopen3(const char* path, int flags, int mode)
|
|
{
|
|
int stat = NC_NOERR;
|
|
int fd = -1;
|
|
char* cvtpath = NULL;
|
|
char* path8 = NULL;
|
|
wchar_t* wpath = NULL;
|
|
|
|
/* First, convert from current Code Page to utf8 */
|
|
if((stat = ansi2utf8(path,&path8))) goto done;
|
|
|
|
if((cvtpath = NCpathcvt(path8))==NULL) goto done;
|
|
|
|
#ifdef _WIN32
|
|
flags |= O_BINARY;
|
|
#endif
|
|
#ifdef WINUTF8
|
|
if(acp == CP_UTF8) {
|
|
/* This should take utf8 directly */
|
|
fd = _open(cvtpath,flags,mode);
|
|
} else
|
|
#endif
|
|
{
|
|
/* Convert from utf8 to wide */
|
|
if((stat = utf82wide(cvtpath,&wpath))) goto done;
|
|
fd = _wopen(wpath,flags,mode);
|
|
}
|
|
|
|
done:
|
|
nullfree(cvtpath);
|
|
nullfree(path8);
|
|
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);
|
|
nullfree(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 stat = 0;
|
|
char* cvtpath = NULL;
|
|
char* path8 = NULL;
|
|
wchar_t* wpath = NULL;
|
|
|
|
/* First, convert from current Code Page to utf8 */
|
|
if((stat = ansi2utf8(path,&path8))) goto done;
|
|
|
|
if((cvtpath = NCpathcvt(path8)) == NULL) {stat = EINVAL; goto done;}
|
|
#ifdef WINUTF8
|
|
if(acp == CP_UTF8) {
|
|
/* This should take utf8 directly */
|
|
if(_access(cvtpath,mode) < 0) {stat = errno; goto done;}
|
|
} else
|
|
#endif
|
|
{
|
|
/* Convert from utf8 to wide */
|
|
if((stat = utf82wide(cvtpath,&wpath))) goto done;
|
|
if(_waccess(wpath,mode) < 0) {stat = errno; goto done;}
|
|
}
|
|
|
|
done:
|
|
nullfree(cvtpath);
|
|
nullfree(path8);
|
|
nullfree(wpath);
|
|
errno = stat;
|
|
return (errno?-1:0);
|
|
}
|
|
|
|
EXTERNL
|
|
int
|
|
NCremove(const char* path)
|
|
{
|
|
int status = 0;
|
|
char* path8 = NULL;
|
|
char* cvtpath = NULL;
|
|
wchar_t* wpath = NULL;
|
|
|
|
/* First, convert from current Code Page to utf8 */
|
|
if((status = ansi2utf8(path,&path8))) goto done;
|
|
|
|
if((cvtpath = NCpathcvt(path8)) == NULL) {status=ENOMEM; goto done;}
|
|
#ifdef WINUTF8
|
|
if(acp == CP_UTF8) {
|
|
/* This should take utf8 directly */
|
|
if(remove(cvtpath) < 0) {status = errno; goto done;}
|
|
} else
|
|
#endif
|
|
{
|
|
if((status = utf82wide(cvtpath,&wpath))) {status = ENOENT; goto done;}
|
|
if(_wremove(wpath) < 0) {status = errno; goto done;}
|
|
}
|
|
|
|
done:
|
|
nullfree(cvtpath);
|
|
nullfree(path8);
|
|
nullfree(wpath);
|
|
errno = status;
|
|
return (errno?-1:0);
|
|
}
|
|
|
|
EXTERNL
|
|
int
|
|
NCmkdir(const char* path, int mode)
|
|
{
|
|
int status = 0;
|
|
char* cvtpath = NULL;
|
|
char* path8 = NULL;
|
|
wchar_t* wpath = NULL;
|
|
|
|
/* First, convert from current Code Page to utf8 */
|
|
if((status = ansi2utf8(path,&path8))) goto done;
|
|
|
|
if((cvtpath = NCpathcvt(path8)) == NULL) {status=ENOMEM; goto done;}
|
|
#ifdef WINUTF8
|
|
if(acp == CP_UTF8) {
|
|
/* This should take utf8 directly */
|
|
if(_mkdir(cvtpath) < 0) {status = errno; goto done;}
|
|
} else
|
|
#endif
|
|
{
|
|
if((status = utf82wide(cvtpath,&wpath))) {status = ENOENT; goto done;}
|
|
if(_wmkdir(wpath) < 0) {status = errno; goto done;}
|
|
}
|
|
|
|
done:
|
|
nullfree(cvtpath);
|
|
nullfree(path8);
|
|
nullfree(wpath);
|
|
errno = status;
|
|
return (errno?-1:0);
|
|
}
|
|
|
|
EXTERNL
|
|
int
|
|
NCrmdir(const char* path)
|
|
{
|
|
int status = 0;
|
|
char* cvtname = NULL;
|
|
char* path8 = NULL;
|
|
|
|
/* First, convert from current Code Page to utf8 */
|
|
if((status = ansi2utf8(path,&path8))) goto done;
|
|
|
|
cvtname = NCpathcvt(path8);
|
|
if(cvtname == NULL) {errno = ENOENT; status = -1;}
|
|
status = rmdir(cvtname);
|
|
done:
|
|
nullfree(cvtname);
|
|
nullfree(path8);
|
|
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;
|
|
char* path8 = NULL;
|
|
wchar_t* wpath = NULL;
|
|
|
|
if((status = ansi2utf8(path,&path8))) goto done;
|
|
|
|
if((cvtpath = NCpathcvt(path8)) == NULL) {status=ENOMEM; goto done;}
|
|
#ifdef WINUTF8
|
|
if(acp == CP_UTF8) {
|
|
if(_stat64(cvtpath,buf) < 0) {status = errno; goto done;}
|
|
} else
|
|
#endif
|
|
{
|
|
if((status = utf82wide(cvtpath,&wpath))) {status = ENOENT; goto done;}
|
|
if(_wstat64(wpath,buf) < 0) {status = errno; goto done;}
|
|
}
|
|
|
|
done:
|
|
nullfree(cvtpath);
|
|
nullfree(path8);
|
|
nullfree(wpath);
|
|
errno = status;
|
|
return (errno?-1:0);
|
|
}
|
|
#endif /*HAVE_SYS_STAT_H*/
|
|
|
|
int
|
|
NCpath2utf8(const char* s, char** u8p)
|
|
{
|
|
return ansi2utf8(s,u8p);
|
|
}
|
|
|
|
int
|
|
NCstdbinary(void)
|
|
{
|
|
int fd;
|
|
fd = _fileno(stdin);
|
|
if(_setmode(fd,_O_BINARY)<0) return NC_EINVAL;
|
|
fd = _fileno(stdout);
|
|
if(_setmode(fd,_O_BINARY)<0) return NC_EINVAL;
|
|
fd = _fileno(stderr);
|
|
if(_setmode(fd,_O_BINARY)<0) return NC_EINVAL;
|
|
return NC_NOERR;
|
|
}
|
|
|
|
#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;
|
|
}
|
|
|
|
int
|
|
NCstdbinary(void)
|
|
{
|
|
return NC_NOERR;
|
|
}
|
|
|
|
#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;
|
|
int platform = NCgetlocalpathkind();
|
|
|
|
assert(path);
|
|
memset(path,0,sizeof(struct Path));
|
|
|
|
if(inpath == NULL) goto done; /* defensive driving */
|
|
if(!pathinitialized) pathinit();
|
|
|
|
#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 */
|
|
}
|
|
/* The /D/x/y/z MSYS paths cause much parsing confusion.
|
|
So only use it if the current platform is windows of mingw.
|
|
Otherwise use windows paths
|
|
*/
|
|
/* X. look for MSYS path /D/... */
|
|
else if((platform== NCPD_WIN || platform == NCPD_MSYS)
|
|
&& len >= 2
|
|
&& (tmp1[0] == '/')
|
|
&& strchr(windrive,tmp1[1]) != NULL
|
|
&& (tmp1[2] == '/' || tmp1[2] == '\0')) {
|
|
/* Assume this is an MSYS 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;
|
|
}
|
|
/* 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 platform)
|
|
{
|
|
int stat = NC_NOERR;
|
|
size_t len;
|
|
char* path = NULL;
|
|
char sdrive[4] = "\0\0\0\0";
|
|
char* p = NULL;
|
|
int cygspecial = 0;
|
|
char 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(platform == NCPD_WIN || platform == 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,platform)
|
|
*/
|
|
|
|
#define CASE(k,t) case ((k)*10+(t))
|
|
|
|
switch (xp->kind*10 + platform) {
|
|
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 + 1); /* strlen("/cygdrive/D") */
|
|
len++; /* nul term */
|
|
if((path = (char*)malloc(len))==NULL)
|
|
{stat = NCTHROW(NC_ENOMEM); goto done;}
|
|
path[0] = '\0';
|
|
if(xp->drive != 0) {
|
|
/* There is no good/standard way to map a windows
|
|
drive letter to a *nix* path.*/
|
|
#if 0
|
|
strlcat(path,"/cygdrive/",len);
|
|
#else
|
|
/* so, just use "/D" where D is the drive letter */
|
|
strlcat(path,"/",len);
|
|
#endif
|
|
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: platform=%s xp=%s path=|%s|\n",NCgetkindname(platform),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);
|
|
nullfree(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(platform) return platform;
|
|
#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 file path from current 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 current 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* path, char** u8p)
|
|
{
|
|
int stat=NC_NOERR;
|
|
char* u8 = NULL;
|
|
int n;
|
|
wchar_t* u16 = NULL;
|
|
|
|
if(path == NULL) goto done;
|
|
|
|
if(!pathinitialized) pathinit();
|
|
|
|
#ifdef WINUTF8
|
|
if(acp == CP_UTF8) { /* Current code page is UTF8 and Windows supports */
|
|
u8 = strdup(path);
|
|
if(u8 == NULL) stat = NC_ENOMEM;
|
|
} else
|
|
#endif
|
|
/* Use wide character conversion plus current code page*/
|
|
{
|
|
/* Get length of the converted string */
|
|
n = MultiByteToWideChar(acp, 0, path, -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, path, -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;}
|
|
}
|
|
done:
|
|
if(u8p) {*u8p = u8; u8 = NULL;}
|
|
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;
|
|
|
|
if(!pathinitialized) pathinit();
|
|
|
|
/* 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;
|
|
|
|
if(!pathinitialized) pathinit();
|
|
|
|
/* 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;
|
|
|
|
if(!pathinitialized) pathinit();
|
|
|
|
/* 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;
|
|
}
|
|
|
|
#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
|