From 2133052c6e49b21af7f9e9003ff477dd5aaf4436 Mon Sep 17 00:00:00 2001 From: Dennis Heimbigner Date: Sat, 30 Nov 2024 17:07:13 -0700 Subject: [PATCH] Simplify FORTRAN access to the new plugin path mechanism The new plugin path API uses char** to represent a variable lenght vector of variable length strings. FORTRAN is not capable of accessing such structures. So, this PR extends the API to provide a counted string-based API to the plugin path functionality. The new functions are inserted in the netcdf_aux.h/daux.c files. The new functions are just wrappers around other plugin path API function; they are just (I hope) more convenient for FORTRAN users. The new functions are as follows: ### *ncaux_plugin_path_stringlen(void)* * Return the length (as in strlen) of the current plugin path directories encoded as a string. Return -1 if the request fails. ### *int ncaux_plugin_path_stringget(int pathlen, char* path)* * Get the current sequence of directories in the internal global plugin path list encoded as a string path using ';' as a path separator. As an example, it might return "/a/b/c;/home/user/me;/tmp". The arguments are as follows: * *pathlen* -- the length of the path argument. * *path* -- a string into which the current plugin path as a string is stored. * Return NC_NOERR | NC_EINVAL ### *int ncaux_plugin_path_stringset(int pathlen, const char* path)* * Set the current sequence of directories in the internal global plugin path list. As an example, it might take "/a/b/c;/home/user/me;/tmp". The arguments are as follows: * *pathlen* -- the length of the path argument. * *path* -- a string that is parsed to obtain the sequence of directories for the current plugin path. * Return NC_NOERR | NC_EINVAL --- include/netcdf_aux.h | 55 +++++++++++++- libdispatch/daux.c | 134 +++++++++++++++++++++++++++++++---- unit_test/Makefile.am | 1 + unit_test/ref_xget.txt | 1 + unit_test/ref_xset.txt | 2 + unit_test/run_pluginpaths.sh | 18 ++++- unit_test/tst_pluginpaths.c | 60 +++++++++++++++- 7 files changed, 254 insertions(+), 17 deletions(-) create mode 100644 unit_test/ref_xget.txt create mode 100644 unit_test/ref_xset.txt diff --git a/include/netcdf_aux.h b/include/netcdf_aux.h index d025ce6aa9..64bf603bf4 100644 --- a/include/netcdf_aux.h +++ b/include/netcdf_aux.h @@ -103,7 +103,7 @@ EXTERNL int ncaux_add_field(void* tag, const char *name, nc_type field_type, struct NCPluginList; /** -Parse a string into a sequence of path directories. +Parse a counted string into a sequence of path directories. The pathlist argument has the following syntax: paths := | dirlist @@ -111,6 +111,7 @@ The pathlist argument has the following syntax: separator := ';' | ':' dir := +@param pathlen length of pathlist arg @param pathlist a string encoding a list of directories @param sep one of ';' | ':' | '\0' where '\0' means use the platform's default separator. @param dirs a pointer to an NCPluginPath object for returning the number and vector of directories from the parse. @@ -121,6 +122,17 @@ will allocate the space for the vector of directory path. The user is then responsible for free'ing that vector (or call ncaux_plugin_path_reclaim). +Author: Dennis Heimbigner +*/ +EXTERNL int ncaux_plugin_path_parsen(size_t pathlen, const char* pathlist, char sep, struct NCPluginList* dirs); + +/** +Parse a nul-terminated string into a sequence of path directories. +@param pathlist a string encoding a list of directories +@param sep one of ';' | ':' | '\0' where '\0' means use the platform's default separator. +@param dirs a pointer to an NCPluginPath object for returning the number and vector of directories from the parse. +@return ::NC_NOERR | NC_EXXX +See also the comments for ncaux_plugin_path_parsen Author: Dennis Heimbigner */ EXTERNL int ncaux_plugin_path_parse(const char* pathlist, char sep, struct NCPluginList* dirs); @@ -192,6 +204,47 @@ Author: Dennis Heimbigner */ EXTERNL int ncaux_plugin_path_prepend(struct NCPluginList* dirs, const char* dir); +/**************************************************/ +/* FORTRAN is not good at manipulating C char** vectors, + so provide some wrappers for use by netcdf-fortran + that read/write plugin path as a single string. + For simplicity, the path separator is always semi-colon. +*/ + +/** + * Return the length (as in strlen) of the current plugin path directories encoded as a string. + * @return length of the string encoded plugin path. + * @author Dennis Heimbigner + * + * @author: Dennis Heimbigner +*/ +EXTERNL int ncaux_plugin_path_stringlen(void); + +/** + * Return the current sequence of directories in the internal global + * plugin path list encoded as a string path using ';' as a path separator. + * @param pathlen the length of the path argument. + * @param path a string into which the current plugin paths are encodeded. + * @return NC_NOERR | NC_EXXX + * @author Dennis Heimbigner + * + * @author: Dennis Heimbigner +*/ +EXTERNL int ncaux_plugin_path_stringget(int pathlen, char* path); + +/** + * Set the current sequence of directories in the internal global + * plugin path list to the sequence of directories encoded as a + * string path using ';' as a path separator. + * @param pathlen the length of the path argument. + * @param path a string encoding the sequence of directories and using ';' to separate them. + * @return NC_NOERR | NC_EXXX + * @author Dennis Heimbigner + * + * @author: Dennis Heimbigner +*/ +EXTERNL int ncaux_plugin_path_stringset(int pathlen, const char* path); + #if defined(__cplusplus) } #endif diff --git a/libdispatch/daux.c b/libdispatch/daux.c index 0d8ce6fe84..71e2a7c524 100644 --- a/libdispatch/daux.c +++ b/libdispatch/daux.c @@ -102,7 +102,7 @@ EXTERNL int ncaux_abort_compound(void* tag) { #ifdef USE_NETCDF4 - int i; + size_t i; struct NCAUX_CMPD* cmpd = (struct NCAUX_CMPD*)tag; if(cmpd == NULL) goto done; if(cmpd->name) free(cmpd->name); @@ -164,7 +164,7 @@ EXTERNL int ncaux_end_compound(void* tag, nc_type* idp) { #ifdef USE_NETCDF4 - int i; + size_t i; int status = NC_NOERR; struct NCAUX_CMPD* cmpd = (struct NCAUX_CMPD*)tag; @@ -247,7 +247,7 @@ getpadding(size_t offset, size_t alignment) static size_t dimproduct(size_t ndims, int* dimsizes) { - int i; + size_t i; size_t product = 1; for(i=0;infields;i++) { struct NCAUX_FIELD* field = &cmpd->fields[i]; - int alignment = 0; + size_t alignment = 0; nc_type firsttype = findfirstfield(cmpd->ncid,field->fieldtype); /* only support 'c' alignment for now*/ @@ -432,7 +432,8 @@ and the parameters themselves (hence the unsigned int** paramsp). EXTERNL int ncaux_h5filterspec_parse(const char* txt, unsigned int* idp, size_t* nparamsp, unsigned int** paramsp) { - int i,stat = NC_NOERR; + int stat = NC_NOERR; + size_t i; char* p; char* sdata0 = NULL; /* what to free */ char* sdata = NULL; /* sdata0 with leading prefix skipped */ @@ -595,7 +596,7 @@ ncaux_h5filterspec_parselist(const char* txt0, int* formatp, size_t* nspecsp, NC p = q + 1; } if(nspecs > 0) { - int count = 0; + size_t count = 0; if((vector = (NC_H5_Filterspec**)calloc(sizeof(NC_H5_Filterspec*),nspecs)) == NULL) {stat = NC_ENOMEM; goto done;} /* pass 2: parse */ @@ -961,6 +962,7 @@ ncaux_dump_data(int ncid, int xtype, void* memory, size_t count, char** bufp) /* Path List Utilities */ /* Path-list Parser: +@param pathlen length of the pathlist0 arg @param pathlist0 the string to parse @param dirs return the parsed directories -- see note below. @param sep the separator parsing: one of ';' | ':' | '\0', where zero means use platform default. @@ -972,19 +974,18 @@ The user is then responsible for free'ing that vector (or call ncaux_plugin_path_reclaim). */ EXTERNL int -ncaux_plugin_path_parse(const char* pathlist0, char sep, NCPluginList* dirs) +ncaux_plugin_path_parsen(size_t pathlen, const char* pathlist0, char sep, NCPluginList* dirs) { int stat = NC_NOERR; size_t i; char* path = NULL; char* p; size_t count; - size_t plen; char seps[2] = "\0\0"; /* will contain all allowable separators */ if(dirs == NULL) {stat = NC_EINVAL; goto done;} - if(pathlist0 == NULL || pathlist0[0] == '\0') {dirs->ndirs = 0; goto done;} + if(pathlen == 0 || pathlist0 == NULL) {dirs->ndirs = 0; goto done;} /* If a separator is specified, use it, otherwise search for ';' or ':' */ seps[0] = sep; @@ -995,10 +996,9 @@ ncaux_plugin_path_parse(const char* pathlist0, char sep, NCPluginList* dirs) else seps[0] = ':'; } - plen = strlen(pathlist0); /* assert plen > 0 */ - if((path = malloc(plen+1+1))==NULL) {stat = NC_ENOMEM; goto done;} - memcpy(path,pathlist0,plen); - path[plen] = '\0'; path[plen+1] = '\0'; /* double null term */ + if((path = malloc(pathlen+1+1))==NULL) {stat = NC_ENOMEM; goto done;} + memcpy(path,pathlist0,pathlen); + path[pathlen] = '\0'; path[pathlen+1] = '\0'; /* double null term */ for(count=0,p=path;*p;p++) { if(strchr(seps,*p) == NULL) @@ -1029,6 +1029,20 @@ ncaux_plugin_path_parse(const char* pathlist0, char sep, NCPluginList* dirs) return stat; } +/* Wrapper around ncaux_plugin_path_parsen +to allow passing a nul-terminated string to parse. +@param pathlist0 the nul-termiated string to parse +@param dirs return the parsed directories -- see note below. +@param sep the separator parsing: one of ';' | ':' | '\0', where zero means use platform default. +@return NC_NOERR || NC_EXXX +See also the comments for ncaux_plugin_path_parsen. +*/ +EXTERNL int +ncaux_plugin_path_parse(const char* pathlist0, char sep, NCPluginList* dirs) +{ + return ncaux_plugin_path_parsen(nulllen(pathlist0),pathlist0,sep,dirs); +} + /* Path-list concatenator where given a vector of directories, concatenate all dirs with specified separator. @@ -1168,3 +1182,95 @@ ncaux_plugin_path_prepend(struct NCPluginList* dirs, const char* dir) done: return stat; } + +/* FORTRAN is not good at manipulating C char** vectors, + so provide some wrappers for use by netcdf-fortran + that read/write plugin path as a single string. + For simplicity, the path separator is always semi-colon. +*/ + +/** + * Return the length (as in strlen) of the current plugin path directories encoded as a string. + * @return length of the string encoded plugin path or -1 if failed. + * @author Dennis Heimbigner + * + * @author: Dennis Heimbigner +*/ +int +ncaux_plugin_path_stringlen(void) +{ + int len = 0; + int stat = NC_NOERR; + struct NCPluginList npl = {0,NULL}; + char* buf = NULL; + + /* Get the list of dirs */ + if((stat = nc_plugin_path_get(&npl))) goto done; + /* Convert to a string path separated by ';' */ + if((stat = ncaux_plugin_path_tostring(&npl,';',&buf))) goto done; + len = nulllen(buf); + +done: + if(npl.dirs != NULL) {(void)ncaux_plugin_path_clear(&npl);} + nullfree(buf); + if(stat) return -1; else return len; +} + +/** + * Return the current sequence of directories in the internal global + * plugin path list encoded as a string path using ';' as a path separator. + * @param pathlen the length of the path argument. + * @param path a string into which the current plugin paths are encodeded. + * @return NC_NOERR | NC_EXXX + * @author Dennis Heimbigner + * + * @author: Dennis Heimbigner +*/ +int +ncaux_plugin_path_stringget(int pathlen, char* path) +{ + int stat = NC_NOERR; + struct NCPluginList npl = {0,NULL}; + char* buf = NULL; + + if(pathlen == 0 || path == NULL) {stat = NC_EINVAL; goto done;} + + /* Get the list of dirs */ + if((stat = nc_plugin_path_get(&npl))) goto done; + /* Convert to a string path separated by ';' */ + if((stat = ncaux_plugin_path_tostring(&npl,';',&buf))) goto done; + strncpy(path,buf,(size_t)pathlen); + +done: + nullfree(buf); + if(npl.dirs != NULL) {(void)ncaux_plugin_path_clear(&npl);} + return stat; +} + +/** + * Set the current sequence of directories in the internal global + * plugin path list to the sequence of directories encoded as a + * string path using ';' as a path separator. + * @param pathlen the length of the path argument. + * @param path a string encoding the sequence of directories and using ';' to separate them. + * @return NC_NOERR | NC_EXXX + * @author Dennis Heimbigner + * + * @author: Dennis Heimbigner +*/ +int +ncaux_plugin_path_stringset(int pathlen, const char* path) +{ + int stat = NC_NOERR; + struct NCPluginList npl = {0,NULL}; + + if(pathlen == 0 || path == NULL) {stat = NC_EINVAL; goto done;} + /* Parse the incoming path */ + if((stat = ncaux_plugin_path_parsen((size_t)pathlen,path,';',&npl))) goto done; + /* set the list of dirs */ + if((stat = nc_plugin_path_set(&npl))) goto done; + +done: + if(npl.dirs != NULL) {(void)ncaux_plugin_path_clear(&npl);} + return stat; +} diff --git a/unit_test/Makefile.am b/unit_test/Makefile.am index 102bb156f9..90eef59c53 100644 --- a/unit_test/Makefile.am +++ b/unit_test/Makefile.am @@ -63,6 +63,7 @@ endif EXTRA_DIST = CMakeLists.txt run_s3sdk.sh run_reclaim_tests.sh run_aws_config.sh run_pluginpaths.sh run_dfaltpluginpath.sh EXTRA_DIST += nctest_netcdf4_classic.nc reclaim_tests.cdl EXTRA_DIST += ref_get.txt ref_set.txt +EXTRA_DIST += ref_xget.txt ref_xset.txt CLEANFILES = reclaim_tests*.txt reclaim_tests.nc tmp_*.txt diff --git a/unit_test/ref_xget.txt b/unit_test/ref_xget.txt new file mode 100644 index 0000000000..100ed6ed92 --- /dev/null +++ b/unit_test/ref_xget.txt @@ -0,0 +1 @@ +testxget(global): /zero;/one;/two;/three;/four diff --git a/unit_test/ref_xset.txt b/unit_test/ref_xset.txt new file mode 100644 index 0000000000..2768b92032 --- /dev/null +++ b/unit_test/ref_xset.txt @@ -0,0 +1,2 @@ +testxset(global): before: /zero;/one;/two;/three;/four +testxset(global): after: /zero;/one;/mod;/two;/three;/four diff --git a/unit_test/run_pluginpaths.sh b/unit_test/run_pluginpaths.sh index 04e5bf1ba9..703090fdf7 100755 --- a/unit_test/run_pluginpaths.sh +++ b/unit_test/run_pluginpaths.sh @@ -86,6 +86,20 @@ testset() { printf "testset(nczarr): after: %s\n" `${TP} -x "set:${DFALT},set:${DFALTSET},get:nczarr"` >> ${filename}.txt } +# Test the ncaux set/get/clear functions */ +testxget() { + filenamefor tmp xget + # print out the global state + printf "testxget(global): %s\n" `${TP} -x "xset:${DFALT},xget:global"` >> ${filename}.txt +} + +testxset() { + filenamefor tmp xset + # print out the global state, modify it and print again + printf "testxset(global): before: %s\n" `${TP} -x "xset:${DFALT},xget:global"` >> ${filename}.txt + printf "testxset(global): after: %s\n" `${TP} -x "xset:${DFALT},xset:${DFALTSET},xget:global"` >> ${filename}.txt +} + ######################### cleanup() { @@ -98,7 +112,7 @@ init() { # Verify output for a specific action verify() { - for action in get set ; do + for action in get set xget xset; do if diff -wBb ${srcdir}/ref_${action}.txt tmp_${action}.txt ; then echo "***PASS: $action" else @@ -111,5 +125,7 @@ verify() { init testget testset +testxget +testxset verify cleanup diff --git a/unit_test/tst_pluginpaths.c b/unit_test/tst_pluginpaths.c index 90a380e7ab..254c8ca161 100644 --- a/unit_test/tst_pluginpaths.c +++ b/unit_test/tst_pluginpaths.c @@ -47,6 +47,10 @@ ACT_GET=1, ACT_SET=2, /* Synthetic Actions */ ACT_CLEAR=3, +/* ncaux commands */ +ACT_XGET=4, +ACT_XSET=5, +ACT_XCLEAR=6, } Action; static struct ActionTable { @@ -57,6 +61,9 @@ static struct ActionTable { {ACT_GET,"get"}, {ACT_SET,"set"}, {ACT_CLEAR,"clear"}, +{ACT_XGET,"xget"}, +{ACT_XSET,"xset"}, +{ACT_XCLEAR,"xclear"}, {ACT_NONE,NULL} }; @@ -101,7 +108,8 @@ static void pluginusage(void) { fprintf(stderr,"usage: tst_pluginpath [-d] -x [:],[:]...\n"); - fprintf(stderr,"\twhere is one of: read | write | clear| or formatx.\n"); + fprintf(stderr,"\twhere is one of: read | write | clear|formatx|xread|xwrite.\n"); + fprintf(stderr,"\t and xread|xwrite use the ncaux API.\n"); fprintf(stderr,"\twhere is arbitrary string (with '\\,' to escape commas); arg can be missing or empty.\n"); exit(1); } @@ -175,10 +183,14 @@ parseactionlist(const char* cmds0) strncpy(cmds,cmds0,cmdlen); /* split into command + formatx + arg strings and count */ ncmds = 0; +#ifdef DEBUG fprintf(stderr,"$$$ cmds=|%s|\n",cmds); +#endif for(leave=0,p=cmds;!leave;p=q) { q = xstrchr(p,','); +#ifdef DEBUG fprintf(stderr,"$$$ p=|%s| q=|%s|\n",p,q); +#endif if(q == NULL) { q = cmds+cmdlen; /* point to trailing nul */ leave = 1; @@ -187,7 +199,9 @@ fprintf(stderr,"$$$ p=|%s| q=|%s|\n",p,q); } ncmds++; } +#ifdef DEBUG fprintf(stderr,"$$$ ncmds=%d\n",(int)ncmds); +#endif if(ncmds > NACTIONS) {fprintf(stderr,"error: -x must have not more than %zu commands.\n",(size_t)NACTIONS); pluginusage();} dumpoptions.nactions = ncmds; /* Now process each command+formatx+arg triple */ @@ -214,7 +228,9 @@ fprintf(stderr,"$$$ ncmds=%d\n",(int)ncmds); descape(dumpoptions.actions[i].name); descape(dumpoptions.actions[i].arg); dumpoptions.actions[i].action = decodeop(dumpoptions.actions[i].name); +#ifdef DEBUG fprintf(stderr,"$$$ [%d] name=|%s| arg=|%s|\n",(int)i,dumpoptions.actions[i].name,dumpoptions.actions[i].arg); +#endif } return; } @@ -292,6 +308,45 @@ actionset(const struct Execute* action) return NCCHECK(stat); } +static int +actionxclear(const struct Execute* action) +{ + int stat = NC_NOERR; + const char* path=""; + if((stat=ncaux_plugin_path_stringset(strlen(path),path))) goto done; +done: + return NCCHECK(stat); +} + +static int +actionxget(const struct Execute* action) +{ + int stat = NC_NOERR; + char path[4096]; + int pathlen = 0; + + /* get pathlen */ + if((pathlen=ncaux_plugin_path_stringlen()) < 0) {stat = NC_EINVAL; goto done;} + /* Get global plugin path */ + if((stat=ncaux_plugin_path_stringget(sizeof(path),path))) goto done; + path[pathlen] = '\0'; /* nul term */ + printf("%s\n",path); + +done: + return NCCHECK(stat); +} + +static int +actionxset(const struct Execute* action) +{ + int stat = NC_NOERR; + + /* set global plugin path */ + if((stat=ncaux_plugin_path_stringset(strlen(action->arg),action->arg))) goto done; +done: + return NCCHECK(stat); +} + int main(int argc, char** argv) { @@ -338,6 +393,9 @@ fprintf(stderr,">>>> [%zu] %s(%d) : %s\n",i, case ACT_CLEAR: if((stat=actionclear(&dumpoptions.actions[i]))) goto done; break; case ACT_GET: if((stat=actionget(&dumpoptions.actions[i]))) goto done; break; case ACT_SET: if((stat=actionset(&dumpoptions.actions[i]))) goto done; break; + case ACT_XCLEAR: if((stat=actionxclear(&dumpoptions.actions[i]))) goto done; break; + case ACT_XGET: if((stat=actionxget(&dumpoptions.actions[i]))) goto done; break; + case ACT_XSET: if((stat=actionxset(&dumpoptions.actions[i]))) goto done; break; } }