Add ability to programmatically

extract info from libnetcdf.settings
API is below.
I have made this API public yet
by adding it to netcdf.h. I will
do that when everyone is agreed on the
proper API.

extern const char* nc_settings(const char* key); /*get value of a specific key */
extern const char** nc_settings_all(); /*get all settings in envv format */
extern void nc_settings_reclaim(); /* reclaim all space and clean up */

Envv format is
{key,value}*,NULL

Also added test: nc_test/tst_settings.c
This commit is contained in:
dmh 2014-08-29 14:51:14 -06:00
parent 056de3f69d
commit be329e7a23
5 changed files with 242 additions and 19 deletions

View File

@ -44,9 +44,22 @@ endif # BUILD_V2
EXTRA_DIST=CMakeLists.txt ncsettings.hdr
# Build ncsettings.c as follows:
# 1. copy ncsettings.hdr to ncsettings.c
# 2. append libnetcdf.settings to ncsettings.c after
# processing it as follows:
# 1. convert tabs and cr to blanks
# 2. convert embedded double quote (") to escaped form (\").
# 3. append newline (\n) to each line
# 4. surround each line with double quotes.
# 3. finally, add a semicolon to the end of ncsettings.c
# to complete the string constant.
ncsettings.c: $(top_srcdir)/libnetcdf.settings ncsettings.hdr
rm -f ncsettings.c
cat ncsettings.hdr > ncsettings.c
sed -e 's/"/\\"/g' <$(top_srcdir)/libnetcdf.settings | sed -e 's/\(.*\)/"\1\\n"/' >> ncsettings.c
tr '\t\r' ' ' <${top_srcdir}/libnetcdf.settings | \
sed -e 's/"/\\"/g' | \
sed -e 's/\(.*\)/\"\1\\n\"/' | \
cat >> ncsettings.c
echo ';' >> ncsettings.c

View File

@ -1,36 +1,245 @@
/*********************************************************************
* Copyright 2014, UCAR/Unidata
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
*********************************************************************/
* Copyright 2014, UCAR/Unidata
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
*********************************************************************/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
static const char* ncsettings;
/* General rule
try to avoid any obscure string functions.
We currently use
- strcasecmp
- strchr
- strndup
- strlen
*/
#undef DEBUG
/* Define the legal leading key characters */
#define KEYCHARS1 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$_."
/*forward*/
static const char* ncsettings_text;
static char** lines;
static int nlines;
static char* dup;
static char** map = NULL;
/*forward*/
static void parse();
static int parseline(const char* line, int keypos);
static int iskeyline(const char* line);
static void preprocess();
const char*
nc_settings(const char* key)
{
char** mapp;
int keylen = strlen(key);
if(map == NULL)
parse();
for(mapp=map;*mapp != NULL;mapp+=2) {
/* Note this assumes that no key is a prefix of another */
if(strncmp(*mapp,key,keylen)==0) {
if(strcasecmp(*mapp,key)==0) {
return mapp[1];
}
}
return NULL;
}
static void parse()
const char**
nc_settings_all()
{
if(map == NULL)
parse();
return (const char**)map;
}
static const char* ncsettings =
const char*
nc_settings_text()
{
return ncsettings_text;
}
static void
parse()
{
int i,keypos;
int nkeys;
const char** line;
preprocess();
nkeys = 0;
/* Count # of key lines */
for(i=0;i<nlines;i++) {
const char* line = lines[i];
#ifdef DEBUG
printf("testing: %s\n",line);
#endif
if(iskeyline(line)) {
#ifdef DEBUG
printf("keyline: %s\n",line);
#endif
nkeys++;
}
}
#ifdef DEBUG
fflush(stdout);
#endif
/* Create the map of proper size */
map = (char**)malloc(((2*nkeys)+1) * sizeof(char*));/*+1 for terminating null*/
if(map == NULL) {
fprintf(stderr,"ncsettings: out of memory\n");
return;
}
map[2*nkeys] = NULL; /* pre-insert terminating null */
/* parse the keylines only */
keypos = 0;
for(i=0;i<nlines;i++) {
const char* line = lines[i];
if(!iskeyline(line)) continue;
if(!parseline(line,keypos))
return;
keypos+=2;
}
}
/*
We assume that each key starts with an alphanumeric character.
*/
static int
parseline(const char* line, int keypos)
{
const char* p;
const char* r;
char* q;
ptrdiff_t delta;
char* key;
char* value;
/* find colon ending of the key */
r = strchr(line,':');
if(r == NULL) { /* malformed */
fprintf(stderr,"malformed libnetcdf.settings file: %s\n,line");
return 0;
}
/* back up from the colon to the first non-blank */
for(p=r;p != line;p--) {
if(*p != ' ') break;
}
if(p == line) {/* empty key */
fprintf(stderr,"malformed libnetcdf.settings file: %s\n,line");
return 0;
}
delta = p - line;
key = strndup(line,delta);
/* skip post ':' blanks */
for(p=r+1;;p++) {
if(*p != ' ') break;
}
if(*p == '\0') /* empty value */
value = strdup("");
else { /* assert value is not empty */
value = strdup(p);
size_t len = strlen(value);
q = value + (len - 1); /* point to last char before trailing nul */
/* back up to the first non-blank */
for(;(*q == ' ');q--); /* will always terminate at value at least */
q[1] = '\0'; /* artificial end */
}
/* Append to the map */
map[keypos] = key;
map[keypos+1] = value;
return 1;
}
/* We assume that each key starts with an alphanumeric character. */
static int
iskeyline(const char* line)
{
if(line == NULL || strlen(line) == 0)
return 0;
if(line[0] == '#' || line[0] == ' ')
return 0;
if(strchr(KEYCHARS1,line[0]) == NULL)
return 0;
return 1;
}
/* We need to process the text as follows:
1. convert tabs and \r to blanks
2. convert \n to EOL (\0)
3. remove leading and trailing blanks from each line
*/
static void
preprocess()
{
int c,i;
const char* p;
char* q;
char* r;
#ifdef DEBUG
printf("input: %s\n",ncsettings_text);
fflush(stdout);
#endif
dup = (char*)malloc(strlen(ncsettings_text)+1);
nlines = 0;
/* steps 1 and 2 */
for(p=ncsettings_text,q=dup;(c=*p);p++) {
switch (c) {
case '\r': case '\t': *q++ = ' '; break;
case '\n': nlines++; *q++ = '\0'; break;
default: *q++ = c; break;
}
}
/* step 3 */
lines = (char**)malloc(nlines*sizeof(char*));
r = dup;
for(i=0;i<nlines;i++) {
int suppress;
lines[i] = r;
r += strlen(r);
r++; /* skip terminating nul */
}
for(i=0;i<nlines;i++) {
char* line = lines[i];
p = line;
for(;(c=*p);p++) {
if(c != ' ')
break;
}
strcpy(line,p); /* remove leading blanks */
q = line+strlen(line); /* terminating nul */
while((c=*(--q))) {
if(c != ' ')
break;
}
/* terminate */
q++;
*q = '\0';
#ifdef DEBUG
printf("processed: %s\n",line);
#endif
}
#ifdef DEBUG
fflush(stdout);
#endif
}
void
nc_settings_reclaim()
{
if(lines != NULL) free(lines);
if(dup != NULL) free(dup);
if(map != NULL) free(map);
lines = NULL;
dup = NULL;
map = NULL;
}
static const char* ncsettings_text =

View File

@ -1,7 +1,7 @@
NetCDF C Configuration Summary
# NetCDF C Configuration Summary
==============================
General
# General
-------
NetCDF Version: @PACKAGE_VERSION@
Configured On: @CONFIG_DATE@
@ -9,7 +9,7 @@ Host System: @host_cpu@-@host_vendor@-@host_os@
Build Directory: @abs_top_builddir@
Install Prefix: @prefix@
Compiling Options
# Compiling Options
-----------------
C Compiler: @CC_VERSION@
CFLAGS: @CFLAGS@
@ -22,7 +22,7 @@ Shared Library: @enable_shared@
Static Library: @enable_static@
Extra libraries: @LIBS@
Features
# Features
--------
NetCDF-2 API: @HAS_NC2@
NetCDF-4 API: @HAS_NC4@

View File

@ -1,10 +1,9 @@
# Test c output
T=tst_diskless3
T=tst_settings
#VG=valgrind --leak-check=full
CFLAGS=-g -O0 -I.. -I../include
LDFLAGS=../liblib/.libs/libnetcdf.a -L/share/ed/local/spock/lib -lhdf5_hl -lhdf5 -lz -lm -lcurl
LD_RUN_PATH=/share/ed/local/spock/lib
LDFLAGS=../liblib/.libs/libnetcdf.a -L/usr/local/lib -lhdf5_hl -lhdf5 -lz -lm -lcurl
# cd .. ; ${MAKE} all

View File

@ -15,8 +15,10 @@ tst_diskless3.nc tst_diskless3_file.cdl tst_diskless3_memory.cdl \
tst_diskless4.cdl tst_diskless4.nc tst_formatx.nc
# These are the tests which are always run.
TESTPROGRAMS = t_nc tst_small nc_test tst_misc tst_norm tst_names \
tst_nofill tst_nofill2 tst_nofill3 tst_atts3
XTESTPROGRAMS = t_nc tst_small nc_test tst_misc tst_norm tst_names \
tst_nofill tst_nofill2 tst_nofill3 tst_atts3 tst_settings
TESTPROGRAMS = tst_settings
if USE_NETCDF4
TESTPROGRAMS += tst_atts