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
This commit is contained in:
Dennis Heimbigner 2024-11-30 17:07:13 -07:00
parent e8d23eaa77
commit 2133052c6e
7 changed files with 254 additions and 17 deletions

View File

@ -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 := <empty> | dirlist
@ -111,6 +111,7 @@ The pathlist argument has the following syntax:
separator := ';' | ':'
dir := <OS specific directory path>
@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

View File

@ -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;i<ndims;i++) product *= (size_t)dimsizes[i];
return product;
@ -256,7 +256,7 @@ dimproduct(size_t ndims, int* dimsizes)
static int
computefieldinfo(struct NCAUX_CMPD* cmpd)
{
int i;
size_t i;
int status = NC_NOERR;
size_t offset = 0;
size_t totaldimsize;
@ -272,7 +272,7 @@ computefieldinfo(struct NCAUX_CMPD* cmpd)
for(offset=0,i=0;i<cmpd->nfields;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 @@ done:
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;
}

View File

@ -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

1
unit_test/ref_xget.txt Normal file
View File

@ -0,0 +1 @@
testxget(global): /zero;/one;/two;/three;/four

2
unit_test/ref_xset.txt Normal file
View File

@ -0,0 +1,2 @@
testxset(global): before: /zero;/one;/two;/three;/four
testxset(global): after: /zero;/one;/mod;/two;/three;/four

View File

@ -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

View File

@ -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 <command>[:<arg>],<command>[:<arg>]...\n");
fprintf(stderr,"\twhere <command> is one of: read | write | clear| or formatx.\n");
fprintf(stderr,"\twhere <command> is one of: read | write | clear|formatx|xread|xwrite.\n");
fprintf(stderr,"\t and xread|xwrite use the ncaux API.\n");
fprintf(stderr,"\twhere <arg> 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 @@ done:
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;
}
}