Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Windows network paths: \\svc\x\y... #2065

Merged
merged 2 commits into from
Aug 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This file contains a high-level description of this package's evolution. Release

## 4.8.1 - TBD

* [Enhancement] Support windows network paths (e.g. \\svc\...). See [Github #2065](https://github.com/Unidata/netcdf-c/issues/2065).
* [Enhancement] Convert to a new representation of the NCZarr meta-data extensions: version 2. Read-only backward compatibility is provided. See [Github #2032](https://github.com/Unidata/netcdf-c/issues/2032).
* [Bug Fix] Fix dimension_separator bug in libnczarr. See [Github #2035](https://github.com/Unidata/netcdf-c/issues/2035).
* [Bug Fix] Fix bugs in libdap4. See [Github #2005](https://github.com/Unidata/netcdf-c/issues/2005).
Expand Down
17 changes: 12 additions & 5 deletions include/ncpathmgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,12 @@ Assumptions about Input path:
1. It is a relative or absolute path
2. It is not a URL
3. It conforms to the format expected by one of the following:
Linux (/x/y/...), Cygwin (/cygdrive/D/...),
Windows (D:/...), or MSYS (/D/...), or relative (x/y...)
Linux (/x/y/...),
Cygwin (/cygdrive/D/...),
Windows (D:\...),
Windows network path (\\mathworks\...)
MSYS (/D/...),
or relative (x/y...)
4. It is encoded in the local platform character set.
Note that for most systems, this is utf-8. But for Windows,
the encoding is most likely some form of ANSI code page, probably
Expand Down Expand Up @@ -107,14 +111,17 @@ that has some desirable properties:
the user is responsible for getting this right.
To this end we choose the linux/cygwin format as our standard canonical form.
If the path has a windows drive letter, then it is represented
in the cygwin "/cygdrive/<drive-letter>" form. If it is on *nix* platform,
then this sequence will never appear and the canonical path will look
like a standard *nix* path.
in the cygwin "/cygdrive/<drive-letter>" form.
If it is a windows network path, then it starts with "//".
If it is on *nix* platform, then this sequence will never appear
and the canonical path will look like a standard *nix* path.
*/
EXTERNL int NCpathcanonical(const char* srcpath, char** canonp);

EXTERNL int NChasdriveletter(const char* path);

EXTERNL int NCisnetworkpath(const char* path);

/* Canonicalize and make absolute by prefixing the current working directory */
EXTERNL char* NCpathabsolute(const char* name);

Expand Down
65 changes: 53 additions & 12 deletions libdispatch/dpathmgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,17 @@ for Windows. Other cases will be added as needed.
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
4. a leading // is a windows network path and is converted
to a drive letter using the fake 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 char* windrive = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@";

static const char netdrive = '@';

static const size_t cdlen = 10; /* strlen("/cygdrive/") */

Expand Down Expand Up @@ -600,6 +604,27 @@ NChasdriveletter(const char* path)
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;
if(canon.kind == NCPD_REL) {
clearPath(&canon);
/* Get the drive letter (if any) from the local wd */
canon.drive = wdpath.drive;
}
isnp = (canon.drive == netdrive);
done:
clearPath(&canon);
return isnp;
}

/**************************************************/
/* Utilities */

Expand All @@ -626,15 +651,27 @@ parsepath(const char* inpath, struct Path* path)
/* Convert to forward slash */
for(p=tmp1;*p;p++) {if(*p == '\\') *p = '/';}

/* parse all paths to 2-parts:
/* 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
/* 1. look for Windows network path //... */
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 MSYS path /D/... */
else if(len >= 2
&& (tmp1[0] == '/')
&& strchr(windrive,tmp1[1]) != NULL
&& (tmp1[2] == '/' || tmp1[2] == '\0')) {
Expand All @@ -649,7 +686,7 @@ parsepath(const char* inpath, struct Path* path)
{stat = NC_ENOMEM; goto done;}
path->kind = NCPD_MSYS;
}
/* 2. Look for leading /cygdrive/D where D is a single-char drive letter */
/* 3. 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
Expand All @@ -666,7 +703,7 @@ parsepath(const char* inpath, struct Path* path)
{stat = NC_ENOMEM; goto done;}
path->kind = NCPD_CYGWIN;
}
/* 3. Look for windows path: D:/... where D is a single-char
/* 4. Look for windows path: D:/... where D is a single-char
drive letter */
else if(len >= 2
&& strchr(windrive,tmp1[0]) != NULL
Expand All @@ -683,14 +720,14 @@ parsepath(const char* inpath, struct Path* path)
{stat = NC_ENOMEM; goto done;}
path->kind = NCPD_WIN;
}
/* look for *nix path */
/* 5. 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 */
} else {/* 6. Relative path of unknown type */
path->kind = NCPD_REL;
path->path = tmp1; tmp1 = NULL;
}
Expand Down Expand Up @@ -748,13 +785,17 @@ unparsepath(struct Path* xp, char** pathp)
break;
case NCPD_WIN:
if(xp->drive == 0) {xp->drive = wdpath.drive;} /*requires a drive */
len = nulllen(xp->path)+2+1;
len = nulllen(xp->path)+2+1+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->drive == netdrive)
strlcat(path,"/",len); /* second slash will come from path */
else {
sdrive[0] = xp->drive;
strlcat(path,sdrive,len);
strlcat(path,":",len);
}
if(xp->path)
strlcat(path,xp->path,len);
/* Convert forward to back */
Expand Down
16 changes: 11 additions & 5 deletions unit_test/test_pathcvt.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ static Test PATHTESTS[] = {
#ifndef _WIN32
/* Test utf8 path */
{"/海/海",{ "/海/海", "/c/海/海", "/cygdrive/c/海/海", "c:\\海\\海"}},
/* Test network path */
{"//git/netcdf-c/dap4_test",{
"/@/git/netcdf-c/dap4_test",
"/@/git/netcdf-c/dap4_test",
"/cygdrive/@/git/netcdf-c/dap4_test",
"\\\\git\\netcdf-c\\dap4_test"}},
#endif
{NULL, {NULL, NULL, NULL, NULL}}
};
Expand All @@ -62,11 +68,11 @@ main(int argc, char** argv)

nc_initialize();

/* Test localkind X path kind */
for(k=0;k<NKINDS;k++) {
int kind = kinds[k];
/* Iterate over the test paths */
for(test=PATHTESTS;test->test;test++) {
/* Test localkind X path-kind */
for(test=PATHTESTS;test->test;test++) {
/* Iterate over the test paths */
for(k=0;k<NKINDS;k++) {
int kind = kinds[k];
/* Compare output for the localkind */
if(test->expected[k] == NULL) {
#ifdef DEBUG
Expand Down