1/********************************************************************* 2 * Copyright 2014, UCAR/Unidata 3 * See netcdf/COPYRIGHT file for copying and redistribution conditions. 4 *********************************************************************/ 5 6#include "config.h" 7#include <stdlib.h> 8#include <stdio.h> 9#include <string.h> 10 11/* General rule 12try to avoid any obscure string functions. 13We currently use 14- strcasecmp 15- strchr 16- strndup 17- strlen 18*/ 19 20#undef DEBUG 21 22/* Define the legal leading key characters */ 23#define KEYCHARS1 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$_." 24 25/*forward*/ 26static const char* ncsettings_text; 27 28static char** lines; 29static int nlines; 30static char* dup; 31static char** map = NULL; 32 33/*forward*/ 34static void parse(); 35static int parseline(const char* line, int keypos); 36static int iskeyline(const char* line); 37static void preprocess(); 38 39const char* 40nc_settings(const char* key) 41{ 42 char** mapp; 43 if(map == NULL) 44 parse(); 45 for(mapp=map;*mapp != NULL;mapp+=2) { 46 /* Note this assumes that no key is a prefix of another */ 47 if(strcasecmp(*mapp,key)==0) { 48 return mapp[1]; 49 } 50 } 51 return NULL; 52} 53 54const char** 55nc_settings_all() 56{ 57 if(map == NULL) 58 parse(); 59 return (const char**)map; 60} 61 62const char* 63nc_settings_text() 64{ 65 return ncsettings_text; 66} 67 68static void 69parse() 70{ 71 int i,keypos; 72 int nkeys; 73 const char** line; 74 75 preprocess(); 76 77 nkeys = 0; 78 /* Count # of key lines */ 79 for(i=0;i<nlines;i++) { 80 const char* line = lines[i]; 81#ifdef DEBUG 82 printf("testing: %s\n",line); 83#endif 84 if(iskeyline(line)) { 85#ifdef DEBUG 86 printf("keyline: %s\n",line); 87#endif 88 nkeys++; 89 } 90 } 91#ifdef DEBUG 92 fflush(stdout); 93#endif 94 /* Create the map of proper size */ 95 map = (char**)malloc(((2*nkeys)+1) * sizeof(char*));/*+1 for terminating null*/ 96 if(map == NULL) { 97 fprintf(stderr,"ncsettings: out of memory\n"); 98 return; 99 } 100 map[2*nkeys] = NULL; /* pre-insert terminating null */ 101 /* parse the keylines only */ 102 keypos = 0; 103 for(i=0;i<nlines;i++) { 104 const char* line = lines[i]; 105 if(!iskeyline(line)) continue; 106 if(!parseline(line,keypos)) 107 return; 108 keypos+=2; 109 } 110} 111 112/* 113We assume that each key starts with an alphanumeric character. 114*/ 115static int 116parseline(const char* line, int keypos) 117{ 118 const char* p; 119 const char* r; 120 char* q; 121 ptrdiff_t delta; 122 char* key; 123 char* value; 124 /* find colon ending of the key */ 125 r = strchr(line,':'); 126 if(r == NULL) { /* malformed */ 127 fprintf(stderr,"malformed libnetcdf.settings file: %s\n,line"); 128 return 0; 129 } 130 /* back up from the colon to the first non-blank */ 131 for(p=r;p != line;p--) { 132 if(*p != ' ') break; 133 } 134 if(p == line) {/* empty key */ 135 fprintf(stderr,"malformed libnetcdf.settings file: %s\n,line"); 136 return 0; 137 } 138 delta = p - line; 139 key = strndup(line,delta); 140 /* skip post ':' blanks */ 141 for(p=r+1;;p++) { 142 if(*p != ' ') break; 143 } 144 if(*p == '\0') /* empty value */ 145 value = strdup(""); 146 else { /* assert value is not empty */ 147 value = strdup(p); 148 size_t len = strlen(value); 149 q = value + (len - 1); /* point to last char before trailing nul */ 150 /* back up to the first non-blank */ 151 for(;(*q == ' ');q--); /* will always terminate at value at least */ 152 q[1] = '\0'; /* artificial end */ 153 } 154 /* Append to the map */ 155 map[keypos] = key; 156 map[keypos+1] = value; 157 return 1; 158} 159 160/* We assume that each key starts with an alphanumeric character. */ 161static int 162iskeyline(const char* line) 163{ 164 if(line == NULL || strlen(line) == 0) 165 return 0; 166 if(line[0] == '#' || line[0] == ' ') 167 return 0; 168 if(strchr(KEYCHARS1,line[0]) == NULL) 169 return 0; 170 return 1; 171} 172 173/* We need to process the text as follows: 1741. convert tabs and \r to blanks 1752. convert \n to EOL (\0) 1763. remove leading and trailing blanks from each line 177*/ 178static void 179preprocess() 180{ 181 int c,i; 182 const char* p; 183 char* q; 184 char* r; 185 186#ifdef DEBUG 187 printf("input: %s\n",ncsettings_text); 188 fflush(stdout); 189#endif 190 dup = (char*)malloc(strlen(ncsettings_text)+1); 191 nlines = 0; 192 /* steps 1 and 2 */ 193 for(p=ncsettings_text,q=dup;(c=*p);p++) { 194 switch (c) { 195 case '\r': case '\t': *q++ = ' '; break; 196 case '\n': nlines++; *q++ = '\0'; break; 197 default: *q++ = c; break; 198 } 199 } 200 /* step 3 */ 201 lines = (char**)malloc(nlines*sizeof(char*)); 202 r = dup; 203 for(i=0;i<nlines;i++) { 204 int suppress; 205 lines[i] = r; 206 r += strlen(r); 207 r++; /* skip terminating nul */ 208 } 209 for(i=0;i<nlines;i++) { 210 char* line = lines[i]; 211 p = line; 212 for(;(c=*p);p++) { 213 if(c != ' ') 214 break; 215 } 216 strcpy(line,p); /* remove leading blanks */ 217 q = line+strlen(line); /* terminating nul */ 218 while((c=*(--q))) { 219 if(c != ' ') 220 break; 221 } 222 /* terminate */ 223 q++; 224 *q = '\0'; 225#ifdef DEBUG 226 printf("processed: %s\n",line); 227#endif 228 } 229#ifdef DEBUG 230 fflush(stdout); 231#endif 232} 233 234void 235nc_settings_reclaim() 236{ 237 if(lines != NULL) free(lines); 238 if(dup != NULL) free(dup); 239 if(map != NULL) free(map); 240 lines = NULL; 241 dup = NULL; 242 map = NULL; 243} 244 245static const char* ncsettings_text = 246