Fix shell handling of escapes

re: https://github.com/Unidata/netcdf-c/issues/1988

There was an issue with certain shell programs (bash notably).
For certain platforms and when given a url that had an escaped
'#' character (e.g. \\#) bash would not remove the backslash. So I
had to add a hack for this. Unfortunately I overdid it and it
removed all '' characters. This is ok for non-windows platforms,
but obviously fails for windows.

The fix is this.

1. In a utility program (ncgen, ncdump, nccopy, etc) there is probably a call (or calls) to NC_backslashUnescape(xxx) where xxx is a path argument from the command line.
2. Replace each such call with NC_shellUnescape(xxx).

The NC_shellUnescape function was added and searched only for occurrences of "\#" and replaces them with "#".
This commit is contained in:
Dennis Heimbigner 2021-04-21 14:59:15 -06:00
parent 67c640f1b8
commit efd1be5d62
10 changed files with 50 additions and 11 deletions

View File

@ -168,5 +168,6 @@ EXTERNL void printutf8hex(const char* s, char* sx);
/* From dutil.c */
EXTERNL char* NC_backslashEscape(const char* s);
EXTERNL char* NC_backslashUnescape(const char* esc);
EXTERNL char* NC_shellUnescape(const char* esc);
#endif /* _NCPATHMGR_H_ */

View File

@ -164,6 +164,37 @@ NC_entityescape(const char* s)
return escaped;
}
char*
/*
Depending on the platform, the shell will sometimes
pass an escaped octotherpe character without removing
the backslash. So this function is appropriate to be called
on possible url paths to unescape such cases. See e.g. ncgen.
*/
NC_shellUnescape(const char* esc)
{
size_t len;
char* s;
const char* p;
char* q;
if(esc == NULL) return NULL;
len = strlen(esc);
s = (char*)malloc(len+1);
if(s == NULL) return NULL;
for(p=esc,q=s;*p;) {
switch (*p) {
case '\\':
if(p[1] == '#')
p++;
/* fall thru */
default: *q++ = *p++; break;
}
}
*q = '\0';
return s;
}
/**
Wrap mktmp and return the generated path,
or null if failed.
@ -371,3 +402,4 @@ int isnan(double x)
}
#endif /*APPLE*/

View File

@ -2412,8 +2412,8 @@ main(int argc, char**argv)
error("one input file and one output file required");
}
/* Canonicalize the input and output files names */
inputfile = NC_backslashUnescape(argv[0]); /* Remove shell added escapes */
outputfile = NC_backslashUnescape(argv[1]);
inputfile = NC_shellUnescape(argv[0]); /* Remove shell added escapes */
outputfile = NC_shellUnescape(argv[1]);
if(strcmp(inputfile, outputfile) == 0) {
error("output would overwrite input");
}

View File

@ -2335,7 +2335,7 @@ main(int argc, char *argv[])
/* We need to look for escape characters because the argument
may have come in via a shell script */
path = NC_backslashUnescape(argv[i]);
path = NC_shellUnescape(argv[i]);
if(path == NULL) {
snprintf(errmsg,sizeof(errmsg),"out of memory un-escaping argument %s", argv[i]);
goto fail;

View File

@ -351,7 +351,7 @@ main(
break;
case 'o': /* to explicitly specify output name */
if(netcdf_name) efree(netcdf_name);
netcdf_name = NC_backslashUnescape(optarg);
netcdf_name = NC_shellUnescape(optarg);
break;
case 'P': /* diskless with persistence */
diskless = 1;
@ -460,7 +460,7 @@ main(
}
}
cdlname = NC_backslashUnescape(argv[0]);
cdlname = NC_shellUnescape(argv[0]);
if(cdlname != NULL) {
if(strlen(cdlname) > NC_MAX_NAME)
cdlname[NC_MAX_NAME] = '\0';

View File

@ -485,7 +485,7 @@ main(int argc, char** argv)
}
{
char* s = NC_backslashUnescape(argv[0]);
char* s = NC_shellUnescape(argv[0]);
strcpy(format.file_name,filenamefor(s));
nullfree(s);
}

View File

@ -163,7 +163,7 @@ main(int argc, char** argv)
dumpoptions.nctype = typefor(optarg);
break;
case 'u': {
char* p = NC_backslashUnescape(optarg);
char* p = NC_shellUnescape(optarg);
ncuriparse(p,&dumpoptions.url);
nullfree(p);
if(dumpoptions.url == NULL) {

View File

@ -129,7 +129,7 @@ getoptions(int* argcp, char*** argvp)
*argvp += optind;
if(*argcp > 0) {
char* p = NC_backslashUnescape((*argvp)[0]);
char* p = NC_shellUnescape((*argvp)[0]);
strcpy(options->file,filenamefor(p));
nullfree(p);
}

View File

@ -177,7 +177,7 @@ main(int argc, char** argv)
}
{
char* p = NC_backslashUnescape(argv[0]);
char* p = NC_shellUnescape(argv[0]);
strcpy(dumpoptions.infile,filenamefor(p));
if(p) free(p);
}

View File

@ -27,6 +27,7 @@ typedef struct Test {
static Test PATHTESTS[] = {
{"/xxx/a/b",{"/xxx/a/b", "/c/xxx/a/b", "/cygdrive/c/xxx/a/b", "c:\\xxx\\a\\b"}},
{"d:/x/y",{ "/d/x/y", "/d/x/y", "/cygdrive/d/x/y", "d:\\x\\y"}},
{"d:\\x\\y",{ "/d/x/y", "/d/x/y", "/cygdrive/d/x/y", "d:\\x\\y"}},
{"/cygdrive/d/x/y",{ "/d/x/y", "/d/x/y", "/cygdrive/d/x/y", "d:\\x\\y"}},
{"/d/x/y",{ "/d/x/y", "/d/x/y", "/cygdrive/d/x/y", "d:\\x\\y"}},
{"/cygdrive/d",{ "/d", "/d", "/cygdrive/d", "d:"}},
@ -38,6 +39,7 @@ static Test PATHTESTS[] = {
"d:\\git\\netcdf-c\\dap4_test\\test_anon_dim.2.syn"}},
/* Test relative path */
{"x/y",{ "x/y", "x/y", "x/y", "x\\y"}},
{"x\\y",{ "x/y", "x/y", "x/y", "x\\y"}},
#ifndef _WIN32
/* Test utf8 path */
{"/海/海",{ "/海/海", "/c/海/海", "/cygdrive/c/海/海", "c:\\\\"}},
@ -54,6 +56,7 @@ main(int argc, char** argv)
Test* test;
int failcount = 0;
char* cvt = NULL;
char* unescaped = NULL;
int k;
int drive = 'c';
@ -71,7 +74,9 @@ main(int argc, char** argv)
#endif
continue;
}
cvt = NCpathcvt_test(test->test,kind,drive);
/* ensure that NC_shellUnescape does not affect result */
unescaped = NC_shellUnescape(test->test);
cvt = NCpathcvt_test(unescaped,kind,drive);
#ifdef DEBUG
fprintf(stderr,"TEST local=%s: input: |%s| expected=|%s| actual=|%s|: ",
kind2string(kind),test->test,test->expected[k],cvt);
@ -95,10 +100,11 @@ main(int argc, char** argv)
#ifdef DEBUG
fprintf(stderr,"\n");
#endif
nullfree(unescaped); unescaped = NULL;
nullfree( cvt); cvt = NULL;
}
}
nullfree(cvt);
nullfree(cvt); nullfree(unescaped);
fprintf(stderr,"%s test_pathcvt\n",failcount > 0 ? "***FAIL":"***PASS");
nc_finalize();
return (failcount > 0 ? 1 : 0);