NAMD
ConfigList.C
Go to the documentation of this file.
1 
7 /*
8  Read in a configuration file of the form:
9  keyword = information\n
10  -or- keyword information\n
11  and produces a database which can return a linked list of strings (char *)
12  to all the information fields associated with that keyword.
13 
14  A "word" is a seqeunce of characters that are not white space (see
15  isspace(3C) ). The equals sign ('=') is optional (though if there is more
16  more than one equals sign, then the 2nd is not ignored). The "information"
17  field may contain more than one word. White space is ignored except that
18  white space between multiple words in the information field is maintained.
19  Everything on the line at and beyond a pound sign ('#') is ignored. Hence
20  a data file can be:
21  fullname = George Washington # the first president of the U.S.
22  fullname = Martha Washington # his second wife
23  Once that is read in, all data associated with "name" can be retreived as
24  StringList *strList = configFile.find("fullname");
25  for (StringList *tmp=strList; tmp!=NULL; tmp = tmp -> next)
26  std::cout << tmp->data << '\n';
27  Note:
28  The returned StringList * is NOT new'ed. Do NOT free it.
29  Keywords are case INsensitive
30 */
31 
32 #include <string.h> // for strncpy, strcasecmp
33 #include <ctype.h> // for isspace
34 #include <stdio.h> // Yes, I do use stdio
35 #include "InfoStream.h"
36 #include "ConfigList.h"
37 #include "common.h"
38 
39 #include "strlib.h" // for strncpy, strcasecmp
40 
41 // given a key word, find the element of theList that matches
42 // This is a private function.
43 ConfigList::ConfigListNode *ConfigList::find_key_word(const char *key) const
44 {
45  ConfigListNode *tmp;
46  for (tmp=theList; tmp!= NULL; tmp = tmp -> next) {
47  if (!strcasecmp(tmp->name, key))
48  return tmp;
49  }
50  return NULL;
51 }
52 
53 // I have a new name/ data pair. If the name does not already exist,
54 // make a new ListData element and put it on the HEAD of theList. If
55 // name does already exist, make a new ListNode and add the name to the end
56 // String 1 starts at s1 and is of length len1
57 // " 2 " " s2 " " " " len2
58 void ConfigList::add_element(const char *s1, int len1, const char *s2, int len2)
59 {
60  char *temps = new char[len1 + 1]; // what is the name?
61  if ( temps == NULL )
62  {
63  NAMD_die("new failed in ConfigList::add_element");
64  }
65 //printf("%s %s\n",s1,s2);
66 #ifndef NAMD_NO_STDOUT_FLUSH
67  fflush(stdout);
68 #endif
69  strncpy(temps, s1, len1);
70  temps[len1] = 0; // terminate the string
71  ConfigListNode *tmpList;
72 
73  tmpList = find_key_word( temps); // go through the list
74  if (tmpList == NULL ) { // if not found
75  tmpList = new ConfigListNode( theList, temps, NULL);// create a new node
76  if ( tmpList == NULL )
77  {
78  NAMD_die("new failed in ConfigList::add_element");
79  }
80  // and stick it on the head of the list
81  theList = tmpList; // note that I can continue to use tmpList
82  }
83 
84  if (len1 < len2) { // if string is smaller, don't realloc
85  delete [] temps;
86  temps = new char[len2 + 1];
87  if ( temps == NULL )
88  {
89  NAMD_die("new failed in ConfigList::add_element");
90  }
91  }
92  strncpy(temps, s2, len2); // get the new string
93  temps[len2] = 0;
94 
95  StringList *newStrList = new StringList(temps); // new element
96  if ( newStrList == NULL )
97  {
98  NAMD_die("new failed in ConfigList::add_element");
99  }
100  newStrList -> next = NULL;
101  if (tmpList -> data == NULL) { // no previous definition
102  tmpList -> data = newStrList; // so this is the new head
103  } else {
104  StringList *tmpStrList = tmpList -> data; // else,
105  while (tmpStrList -> next != NULL) // find the end of the list
106  tmpStrList = tmpStrList -> next;
107  tmpStrList -> next = newStrList; // and stick it on the tail
108  }
109 
110  delete [] temps;
111 }
112 
113 
114 // The scripting interface will call our add_element routine.
115 
117 {
118  isokay = TRUE;
119  theList = NULL;
120 }
121 
122 // Used to implement "source" for file parser below.
123 
124 struct FileStack {
125  FILE *file;
127  char *filename;
129 };
130 
131 // open, read, parse, and close the file
132 // make a linked list of "AssocList"
133 // The file is parsed as (I think):
134 // [w]?([W])*[w]?[=]?[w]?([W][wW])?[w]
135 // where w == white space; W is non-whitespace
136 // [x]? says that there can be 0 or more characters of type x.
137 // [x]* says there must be at least 1 character of type x
138 // the terms in () are the two that are used. Everything after a '#'
139 // sign is excluded. I'm not sure how to specify that exclusion in
140 // my regular expression
141 
142 // a new one: if the first character of the data is '{'
143 // then I append new keyword values for each line until I get to
144 // a line with the first non-blank character as a '}'
145 ConfigList::ConfigList(const char *filename_in)
146 {
147  char *filename = new char[strlen(filename_in)+1];
148  strcpy(filename,filename_in);
149  FileStack *fileStack = 0;
150  FILE *infile;
151 
152  isokay = FALSE;
153  theList = NULL;
154  int linenumber = 0; // keep track of line numbers for searching out errors
155 
156  if (!strcmp(filename,"-")) { // should the input be from stdin?
157  infile = stdin;
158  } else {
159  if ( (infile = Fopen(filename, "r")) == NULL ) {
160  iout << iWARN << "Unable to open configuration file '"
161  << filename << "'.\n" << endi;
162  isokay = FALSE;
163  return;
164  }
165  }
166  isokay = TRUE; // file is now open
167 
168  // so read and parse it
169  char buf[1000]; // give myself lots of space
170  char *namestart, *nameend, *datastart, *dataend;
171  char *s;
172  int spacecount;
173  int fileok;
174  while ((fileok = ! ! fgets(buf, 999, infile)) || fileStack) {
175  if ( fileStack && ! fileok ) { // done with "source"
176  delete [] filename;
177  filename = fileStack->filename;
178  linenumber = fileStack->linenumber;
179  infile = fileStack->file;
180  FileStack *delStack = fileStack;
181  fileStack = fileStack->next;
182  delete delStack;
183  continue;
184  }
185  linenumber ++;
186  namestart = nameend = datastart = dataend = NULL;
187  spacecount = 0;
188 
189  for (s = buf; *s && *s!='\n'; s++) { // get to the end of the line
190  if (*s == '#') // found a comment, so break
191  break;
192  if ( !isspace(*s) ) // dataend will always be the last non-blank char
193  dataend = s;
194  if ( !isspace(*s) && !namestart) // found first character of name
195  {namestart = s; continue; }
196  if ( (isspace(*s) || *s == '=') && // found last character of name
197  namestart && !nameend)
198  nameend = s - 1;
199  if ( !isspace(*s) && !datastart && // found the next char. after name
200  nameend)
201  if (*s == '=' && spacecount == 0) // an equals is allowed
202  {spacecount++; continue; } // but only once
203  else
204  {datastart = s; continue; } // otherwise, use it
205  }
206  if (*s == '\n') // cut out the newline at the end
207  *s = 0;
208  else
209  if (*s != '#') { // then there was an overflow
210  iout << iWARN << "Line " << linenumber << " of configuration file "
211  << filename << " contains more than 999 characters."
212  << " Excess characters will be ignored.\n" << endi;
213  } else {
214  *s = 0; // delete the '#' character
215  }
216 
217 // I will also ignore line that I can't understand
218 // If there is any text on the line (as compared to a blank or commented)
219 // line, then I will say that there is a problem.
220  if (!namestart || !nameend || !datastart || !dataend) {
221  if (!namestart && datastart || namestart && !datastart) {// was some data
222  iout << iWARN << "Couldn't parse line " << linenumber << " in "
223  << "configuration file " << filename << ". The line was: "
224  << buf << "\n" << endi;
225  }
226  continue; // which ever the case, go to the next line
227  }
228 
229  if ( ! strncmp(namestart, "source", nameend-namestart+1) ) {
230  // see if the the name is "source"
231 
232  // store the old file data
233  FileStack *newStack = new FileStack;
234  newStack->filename = filename;
235  newStack->linenumber = linenumber;
236  newStack->file = infile;
237  newStack->next = fileStack;
238  fileStack = newStack;
239 
240  // copy the filename
241  char *cpychar = new char[dataend-datastart+2];
242  strcpy(cpychar,datastart);
243  filename = cpychar;
244 
245  // open the sourced file
246  if ( (infile = Fopen(filename, "r")) == NULL ) {
247  iout << iWARN << "Unable to open file '"
248  << filename << "' sourced by '"
249  << fileStack->filename << "' at line "
250  << fileStack->linenumber << ".\n" << endi;
251  isokay = FALSE;
252  return;
253  }
254  iout << iINFO << "Sourcing " << filename << "\n" << endi;
255  isokay = TRUE; // file is now open
256  linenumber = 0;
257 
258  } else if (datastart[0] == '{') {
259  // check if the data begins with a '{'
260  // will remove initial '{' and final '}' to match Tcl.
261  std::ostringstream alldata;
262  char newdata[1000];
263  int found_end = 0;
264  ++datastart; // remove initial '{'
265  int open_brace_count = 1;
266  char *newline = datastart;
267  strcat(newline,"\n"); // put back newline that was removed above
268 
269  while ( 1 ) {
270  int i;
271  int escape_next = 0;
272  for (i=0; i<1000; i++) {
273  if (! newline[i]) {
274  break;
275  }
276  if (escape_next) {
277  escape_next = 0;
278  continue;
279  }
280  if (newline[i] == '\\' && ! escape_next) {
281  escape_next = 1;
282  }
283  if (newline[i] == '{' && ! escape_next) {
284  ++open_brace_count;
285  }
286  if (newline[i] == '}' && ! escape_next) {
287  if ( ( found_end = ! --open_brace_count ) ) {
288  newline[i] = 0;
289  break;
290  }
291  }
292  }
293  alldata << newline;
294  if (found_end) break;
295  newline = newdata;
296  if ( ! fgets(newdata, 999, infile) ) break;
297  linenumber ++;
298  }
299  std::string alldatastr = alldata.str();
300  add_element(namestart, nameend-namestart+1, alldatastr.c_str(), alldata.str().length());
301  // delete string?
302  if (!found_end) {
303  *(nameend+1) = 0;
304  sprintf(newdata, "configuration file ended early while parsing line "
305  "%d of keyword structure %s", linenumber, namestart);
306  NAMD_die(newdata);
307  }
308  } else {
309  // now I can add the new values to the linked list
310  add_element( namestart, nameend - namestart + 1, datastart,
311  dataend - datastart + 1 );
312  }
313  } // while I can still get data with fgets
314 
315  if (strcmp(filename,"-")) { // close the input file if not stdin
316  Fclose(infile);
317  }
318  delete [] filename;
319 }
320 
321 // destructor for the class - just delete a linked list
323 {
324  ConfigListNode *curr=theList, *next=NULL;
325 
326  while ( curr!=NULL )
327  {
328  next = curr -> next;
329  delete curr; // the nasties for deleted the linked list of string
330  // has already been defined in the typedef struct
331  // for ConfigListNode
332  curr = next;
333  }
334 
335  theList = NULL;
336 } // all done
337 
338 
339 // Given "name", find all the elements of the ConfigList that
340 // have have that "name"
341 StringList *ConfigList::find(const char *name) const
342 {
343  ConfigListNode *tmpList;
344  tmpList = find_key_word(name);
345  if (tmpList != NULL)
346  return tmpList -> data;
347  return NULL;
348 }
349 
350 //#define TEST_CONFIGLIST_C
351 #ifdef TEST_CONFIGLIST_C
352 #include <iostream.h>
353 
354 main()
355 {
356  ConfigList dat("namd.conf"); // an example file
357  StringList *strings;
358  int i;
359 
360  if (!dat.okay())
361  NAMD_die("Can't continue - cannot get info from file.");
362  std::cout << "Searching for: 'fullname'\n";
363  strings = dat.find("fullname");
364  if (!strings) {
365  NAMD_die("Couldn't find fullname.\n");
366  }
367  i=0;
368  while (strings) { // write all the data associated with 'fullname'
369  std::cout << i << " = " << strings->data << '\n';
370  strings = strings -> next;
371  i++;
372  }
373 }
374 
375 #endif
376 
char * filename
Definition: ConfigList.C:127
std::ostream & iINFO(std::ostream &s)
Definition: InfoStream.C:81
void add_element(const char *s1, int len1, const char *s2, int len2)
Definition: ConfigList.C:58
int linenumber
Definition: ConfigList.C:126
std::ostream & endi(std::ostream &s)
Definition: InfoStream.C:54
#define FALSE
Definition: common.h:118
std::ostream & iWARN(std::ostream &s)
Definition: InfoStream.C:82
~ConfigList(void)
Definition: ConfigList.C:322
#define iout
Definition: InfoStream.h:51
static Units next(Units u)
Definition: ParseOptions.C:48
FILE * Fopen(const char *filename, const char *mode)
Definition: common.C:273
FILE * file
Definition: ConfigList.C:125
void NAMD_die(const char *err_msg)
Definition: common.C:85
int Fclose(FILE *fout)
Definition: common.C:367
char * data
Definition: ConfigList.h:48
int main(int argc, char *argv[])
Definition: diffbinpdb.c:15
StringList * find(const char *name) const
Definition: ConfigList.C:341
ConfigList(void)
Definition: ConfigList.C:116
FileStack * next
Definition: ConfigList.C:128
#define TRUE
Definition: common.h:119