From de23473ac30aefbc48b6833ac53b6ab1cd91f056 Mon Sep 17 00:00:00 2001 From: Dennis Heimbigner Date: Mon, 9 Aug 2021 15:34:23 -0600 Subject: [PATCH 1/2] Support Windows network paths: \\svc\x\y... re: Issue https:\\github.com\Unidata\netcdf-c\issues\2060 The path conversion code forgot to consider the case of windows network paths of the form \\svc\x\y... I have added support for it, but I can't really test it since I do not have access to a network drive. --- include/ncpathmgr.h | 17 +++++++---- libdispatch/dpathmgr.c | 65 ++++++++++++++++++++++++++++++++-------- unit_test/test_pathcvt.c | 16 ++++++---- 3 files changed, 76 insertions(+), 22 deletions(-) diff --git a/include/ncpathmgr.h b/include/ncpathmgr.h index c51c9bd20d..dd9faea4e5 100644 --- a/include/ncpathmgr.h +++ b/include/ncpathmgr.h @@ -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 @@ -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/" 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/" 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); diff --git a/libdispatch/dpathmgr.c b/libdispatch/dpathmgr.c index 15c61536c4..eb450534e0 100644 --- a/libdispatch/dpathmgr.c +++ b/libdispatch/dpathmgr.c @@ -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/") */ @@ -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 */ @@ -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')) { @@ -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 @@ -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 @@ -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; } @@ -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 */ diff --git a/unit_test/test_pathcvt.c b/unit_test/test_pathcvt.c index e1725aa637..98d9422091 100644 --- a/unit_test/test_pathcvt.c +++ b/unit_test/test_pathcvt.c @@ -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}} }; @@ -62,11 +68,11 @@ main(int argc, char** argv) nc_initialize(); - /* Test localkind X path kind */ - for(k=0;ktest;test++) { + /* Test localkind X path-kind */ + for(test=PATHTESTS;test->test;test++) { + /* Iterate over the test paths */ + for(k=0;kexpected[k] == NULL) { #ifdef DEBUG From 01487ecbf11c743ba4097dd03cf632dfa3134307 Mon Sep 17 00:00:00 2001 From: Dennis Heimbigner Date: Mon, 9 Aug 2021 15:37:42 -0600 Subject: [PATCH 2/2] Update release notes --- RELEASE_NOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 11f68bc91b..b69a8966c1 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -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).