/********************************************************************* * 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 =