netcdf-c/libdispatch/ncsettings.hdr
dmh be329e7a23 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
2014-08-29 14:51:14 -06:00

246 lines
5.0 KiB
Plaintext

/*********************************************************************
* 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>
/* 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;
if(map == NULL)
parse();
for(mapp=map;*mapp != NULL;mapp+=2) {
/* Note this assumes that no key is a prefix of another */
if(strcasecmp(*mapp,key)==0) {
return mapp[1];
}
}
return NULL;
}
const char**
nc_settings_all()
{
if(map == NULL)
parse();
return (const char**)map;
}
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 =