netcdf-c/libdispatch/dpathmgr.c
Dennis Heimbigner 0b7a5382e7 Codify cross-platform file paths
The netcdf-c code has to deal with a variety of platforms:
Windows, OSX, Linux, Cygwin, MSYS, etc.  These platforms differ
significantly in the kind of file paths that they accept.  So in
order to handle this, I have created a set of replacements for
the most common file system operations such as _open_ or _fopen_
or _access_ to manage the file path differences correctly.

A more limited version of this idea was already implemented via
the ncwinpath.h and dwinpath.c code. So this can be viewed as a
replacement for that code. And in path in many cases, the only
change that was required was to replace '#include <ncwinpath.h>'
with '#include <ncpathmgt.h>' and then replace file operation
calls with the NCxxx equivalent from ncpathmgr.h Note that
recently, the ncwinpath.h was renamed ncpathmgmt.h, so this pull
request should not require dealing with winpath.

The heart of the change is include/ncpathmgmt.h, which provides
alternate operations such as NCfopen or NCaccess and which properly
parse and rebuild path arguments to work for the platform on which
the code is executing. This mostly matters for Windows because of the
way that it uses backslash and drive letters, as compared to *nix*.
One important feature is that the user can do string manipulations
on a file path without having to worry too much about the platform
because the path management code will properly handle most mixed cases.
So one can for example concatenate a path suffix that uses forward
slashes to a Windows path and have it work correctly.

The conversion code is in libdispatch/dpathmgr.c, and the
important function there is NCpathcvt which does the proper
conversions to the local path format.

As a rule, most code should just replace their file operations with
the corresponding NCxxx ones defined in include/ncpathmgmt.h. These
NCxxx functions all call NCpathcvt on their path arguments before
executing the actual file operation.

In some rare cases, the client may need to directly use NCpathcvt,
but this should be avoided as much as possible. If there is a need
for supporting a new file operation not already in ncpathmgmt.h, then
use the code in dpathmgr.c as a template. Also please notify Unidata
so we can include it as a formal part or our supported operations.
Also, if you see an operation in the library that is not using the
NCxxx form, then please submit an issue so we can fix it.

Misc. Changes:
* Clean up the utf8 testing code; it is impossible to get some
  tests to work under windows using shell scripts; the args do
  not pass as utf8 but as some other encoding.
* Added an extra utf8 test case: test_unicode_path.sh
* Add a true test for HDF5 1.10.6 or later because as noted in
  PR https://github.com/Unidata/netcdf-c/pull/1794,
  HDF5 changed its Windows file path handling.
2021-03-04 13:41:31 -07:00

944 lines
22 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 PATHFORMAT
#ifdef _WIN32
#define access _access
#define mkdir _mkdir
#define rmdir _rmdir
#define getcwd _getcwd
#endif
/*
Code to provide some path conversion code so that
cygwin and (some) mingw paths can be passed to open/fopen
for Windows. Other cases will be added as needed.
Rules:
1. a leading single alpha-character path element (e.g. /D/...)
will be interpreted as a windows drive letter.
2. a leading '/cygdrive/X' will be converted to
a drive letter X if X is alpha-char.
3. a leading D:/... is treated as a windows drive letter
5. If any of the above is encountered, then forward slashes
will be converted to backslashes.
All other cases are passed thru unchanged
*/
/* Define legal windows drive letters */
static const char* windrive = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
static const size_t cdlen = 10; /* strlen("/cygdrive/") */
static int pathinitialized = 0;
static int pathdebug = -1;
static const struct Path {
int kind;
int drive;
char* path;
} empty = {NCPD_UNKNOWN,0,NULL};
/* Keep the working directory kind and drive */
static struct Path wdpath = {NCPD_UNKNOWN,0,NULL};
static char wdstaticpath[8192];
static int parsepath(const char* inpath, struct Path* path);
static int unparsepath(struct Path* p, char** pathp);
static int getwdpath(struct Path* wd);
static char* printPATH(struct Path* p);
static int getlocalpathkind(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
EXTERNL
char* /* caller frees */
NCpathcvt(const char* inpath)
{
int stat = NC_NOERR;
char* tmp1 = NULL;
struct Path canon = empty;
if(inpath == NULL) goto done; /* defensive driving */
if(!pathinitialized) pathinit();
if(testurl(inpath)) { /* Pass thru URLs */
if((tmp1 = strdup(inpath))==NULL) stat = NC_ENOMEM;
goto done;
}
if((stat = parsepath(inpath,&canon))) {goto done;}
/* Special check for special cygwin paths: /tmp,usr etc */
if(getlocalpathkind() == NCPD_CYGWIN
&& iscygwinspecial(canon.path)
&& canon.kind == NCPD_NIX)
canon.kind = NCPD_CYGWIN;
if(canon.kind != NCPD_REL && wdpath.kind != canon.kind) {
nclog(NCLOGWARN,"NCpathcvt: path mismatch: platform=%d inpath=%d\n",
wdpath.kind,canon.kind);
canon.kind = wdpath.kind; /* override */
}
if((stat = unparsepath(&canon,&tmp1))) {goto done;}
done:
if(pathdebug) {
fprintf(stderr,"xxx: inpath=|%s| outpath=|%s|\n",
inpath?inpath:"NULL",tmp1?tmp1:"NULL");
fflush(stderr);
}
if(stat) {
nullfree(tmp1); tmp1 = NULL;
nclog(NCLOGERR,"NCpathcvt: stat=%d (%s)",
stat,nc_strerror(stat));
}
clearPath(&canon);
return tmp1;
}
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();
/* Canonicalize relpath */
if((stat = parsepath(relpath,&canon))) {goto done;}
/* See if relative */
if(canon.kind == NCPD_REL) {
/* prepend the wd path to the inpath, including drive letter, if any */
len = strlen(wdpath.path)+strlen(canon.path)+1+1;
if((tmp1 = (char*)malloc(len))==NULL)
{stat = NC_ENOMEM; {goto done;}}
tmp1[0] = '\0';
strlcat(tmp1,wdpath.path,len);
strlcat(tmp1,"/",len);
strlcat(tmp1,canon.path,len);
nullfree(canon.path);
canon.path = tmp1; tmp1 = NULL;
canon.drive = wdpath.drive;
canon.kind = wdpath.kind;
}
/* rebuild */
if((stat=unparsepath(&canon,&result))) goto done;
done:
if(pathdebug) {
fprintf(stderr,"xxx: 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 */
/* Force drive and wd before invoking NCpathcvt
and then revert */
EXTERNL
char* /* caller frees */
NCpathcvt_test(const char* inpath, int ukind, int udrive)
{
char* result = NULL;
struct Path oldwd = wdpath;
if(!pathinitialized) pathinit();
/* Override */
wdpath.kind = ukind;
wdpath.drive = udrive;
wdpath.path = strdup("/");
if(pathdebug)
fprintf(stderr,"xxx: wd=|%s|",printPATH(&wdpath));
result = NCpathcvt(inpath);
clearPath(&wdpath);
wdpath = oldwd;
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(&wdpath);
/* make the path static but remember to never free it (Ugh!) */
wdstaticpath[0] = '\0';
strlcat(wdstaticpath,wdpath.path,sizeof(wdstaticpath));
clearPath(&wdpath);
wdpath.path = wdstaticpath;
pathinitialized = 1;
}
static void
clearPath(struct Path* path)
{
nullfree(path->path);
path->path = NULL;
}
static const char* cygwinspecial[] =
{"/bin/","/dev/","/etc/","/home/",
"/lib/","/proc/","/sbin/","/tmp/",
"/usr/","/var/",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* cvtpath = NULL;
wchar_t* wpath = NULL;
wchar_t* wflags = NULL;
cvtpath = NCpathcvt(path);
if(cvtpath == NULL) return NULL;
/* Convert from local to wide */
if((stat = utf82wide(cvtpath,&wpath))) goto done;
if((stat = ansi2wide(flags,&wflags))) goto done;
f = _wfopen(wpath,wflags);
done:
nullfree(cvtpath);
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;
wchar_t* wpath = NULL;
cvtpath = NCpathcvt(path);
if(cvtpath == NULL) goto done;
/* Convert from utf8 to wide */
if((stat = utf82wide(cvtpath,&wpath))) goto done;
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;
char* cvtname = NCpathcvt(path);
if(cvtname == NULL) {errno = ENOENT; return -1;}
stat = closedir(cvtname);
free(cvtname);
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;
struct Path wd = empty;
char* path = NULL;
size_t len;
errno = 0;
if(cwdlen == 0) {status = ENAMETOOLONG; goto done;}
if(!pathinitialized) pathinit();
if((status = getwdpath(&wd))) {status = ENOENT; goto done;}
if((status = unparsepath(&wd,&path))) {status = EINVAL; goto done;}
len = strlen(path);
if(len >= cwdlen) {status = ENAMETOOLONG; goto done;}
if(cwdbuf == NULL) {
if((cwdbuf = malloc(cwdlen))==NULL) {status = ENOMEM; goto done;}
}
memcpy(cwdbuf,path,len+1);
done:
clearPath(&wd);
nullfree(path);
errno = status;
return cwdbuf;
}
#ifdef HAVE_SYS_STAT_H
EXTERNL
int
NCstat(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(_wstat(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;
if(canon.kind == NCPD_REL) {
clearPath(&canon);
/* Get the drive letter (if any) from the local wd */
canon.drive = wdpath.drive;
}
hasdl = (canon.drive != 0);
done:
clearPath(&canon);
return hasdl;
}
/**************************************************/
/* 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 */
/* Convert to UTF8 */
#if 0
if((stat = NCpath2utf8(inpath,&tmp1))) goto done;
#else
tmp1 = strdup(inpath);
#endif
/* Convert to forward slash */
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 MSYS path /D/... */
if(len >= 2
&& (tmp1[0] == '/')
&& strchr(windrive,tmp1[1]) != NULL
&& (tmp1[2] == '/' || tmp1[2] == '\0')) {
/* Assume this is a mingw 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;
}
/* 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;
}
/* 3. 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;
}
/* look for *nix path */
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 {/* 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 stat = NC_NOERR;
size_t len;
char* path = NULL;
char sdrive[2] = {'\0','\0'};
char* p = NULL;
int kind = xp->kind;
int cygspecial = 0;
switch (kind) {
case NCPD_NIX:
len = nulllen(xp->path);
if(xp->drive != 0) {
len += 2;
sdrive[0] = xp->drive;
}
len++; /* nul terminate */
if((path = (char*)malloc(len))==NULL)
{stat = NC_ENOMEM; goto done;}
path[0] = '\0';
if(xp->drive != 0) {
strlcat(path,"/",len);
strlcat(path,sdrive,len);
}
if(xp->path != NULL)
strlcat(path,xp->path,len);
break;
case NCPD_CYGWIN:
/* Is this one of the special cygwin paths? */
cygspecial = iscygwinspecial(xp->path);
if(xp->drive == 0) {xp->drive = wdpath.drive;} /*may require a drive */
len = nulllen(xp->path)+cdlen+1+1;
if((path = (char*)malloc(len))==NULL)
{stat = NC_ENOMEM; goto done;}
path[0] = '\0';
if(!cygspecial) {
strlcat(path,"/cygdrive/",len);
sdrive[0] = xp->drive;
strlcat(path,sdrive,len);
}
if(xp->path)
strlcat(path,xp->path,len);
break;
case NCPD_WIN:
if(xp->drive == 0) {xp->drive = wdpath.drive;} /*requires a drive */
len = nulllen(xp->path)+2+1;
if((path = (char*)malloc(len))==NULL)
{stat = NC_ENOMEM; goto done;}
path[0] = '\0';
sdrive[0] = xp->drive;
strlcat(path,sdrive,len);
strlcat(path,":",len);
if(xp->path)
strlcat(path,xp->path,len);
/* Convert forward to back */
for(p=path;*p;p++) {if(*p == '/') *p = '\\';}
break;
case NCPD_MSYS:
if(xp->drive == 0) {xp->drive = wdpath.drive;} /*requires a drive */
len = nulllen(xp->path)+2+1;
if((path = (char*)malloc(len))==NULL)
{stat = NC_ENOMEM; goto done;}
path[0] = '\0';
sdrive[0] = xp->drive;
strlcat(path,"/",len);
strlcat(path,sdrive,len);
if(xp->path)
strlcat(path,xp->path,len);
break;
case NCPD_REL:
path = strdup(xp->path);
/* Use wdpath to decide slashing */
if(wdpath.kind == NCPD_WIN) {
/* Convert forward to back */
for(p=path;*p;p++) {if(*p == '/') *p = '\\';}
}
break;
default: stat = NC_EINTERNAL; goto done;
}
if(pathp) {*pathp = path; path = NULL;}
done:
nullfree(path);
return stat;
}
static int
getwdpath(struct Path* wd)
{
int stat = NC_NOERR;
char* path = NULL;
if(wd->path != NULL) return stat;
memset(wd,0,sizeof(struct Path));
{
#ifdef _WIN32
wchar_t* wpath = NULL;
wpath = _wgetcwd(NULL,8192);
if((stat = wide2utf8(wpath,&path)))
{nullfree(wpath); wpath = NULL; return stat;}
#else
path = getcwd(NULL,8192);
#endif
}
stat = parsepath(path,wd);
/* Force the kind */
wd->kind = getlocalpathkind();
nullfree(path); path = NULL;
return stat;
}
static int
getlocalpathkind(void)
{
int kind = NCPD_UNKNOWN;
#ifdef __CYGWIN__
kind = NCPD_CYGWIN;
#elif __MSYS__
kind = NCPD_MSYS;
#elif _MSC_VER /* not _WIN32 */
kind = NCPD_WIN;
#else
kind = NCPD_NIX;
#endif
return kind;
}
#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 = 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 = 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);
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 = 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 = 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 = 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';
}
/**************************************************/
#if 0
#ifdef HAVE_DIRENT_H
EXTERNL
DIR*
NCopendir(const char* path)
{
DIR* ent = NULL;
char* cvtpath = NCpathcvt(path);
if(cvtpath == NULL) return -1;
ent = opendir(cvtpath);
free(cvtpath);
return ent;
}
EXTERNL
int
NCclosedir(DIR* ent)
{
int stat = 0;
char* cvtpath = NCpathcvt(path);
if(cvtpath == NULL) return -1;
stat = closedir(cvtpath);
free(cvtpath);
return stat;
}
#endif
#endif /*0*/