ConfigList.C

Go to the documentation of this file.
00001 
00007 /*
00008      Read in a configuration file of the form:
00009          keyword = information\n
00010    -or-  keyword information\n
00011    and produces a database which can return a linked list of strings (char *)
00012    to all the information fields associated with that keyword.
00013    
00014       A "word" is a seqeunce of characters that are not white space (see
00015    isspace(3C) ).  The equals sign ('=') is optional (though if there is more
00016    more than one equals sign, then the 2nd is not ignored).  The "information"
00017    field may contain more than one word.  White space is ignored except that
00018    white space between multiple words in the information field is maintained.
00019    Everything on the line at and beyond a pound sign ('#') is ignored.  Hence
00020    a data file can be:
00021      fullname = George   Washington # the first president of the U.S.
00022      fullname = Martha Washington   # his second wife
00023    Once that is read in, all data associated with "name" can be retreived as
00024     StringList *strList = configFile.find("fullname");
00025     for (StringList *tmp=strList; tmp!=NULL; tmp = tmp -> next)
00026         std::cout << tmp->data << '\n';
00027    Note:
00028      The returned StringList * is NOT new'ed.  Do NOT free it.
00029      Keywords are case INsensitive
00030 */
00031 
00032 #include <string.h> // for strncpy, strcasecmp
00033 #include <ctype.h>   // for isspace
00034 #include <stdio.h>   // Yes, I do use stdio
00035 #include "InfoStream.h"
00036 #include "ConfigList.h"
00037 #include "common.h"
00038 
00039 #include "strlib.h"     // for strncpy, strcasecmp
00040 
00041 // given a key word, find the element of theList that matches
00042 // This is a private function.
00043 ConfigList::ConfigListNode *ConfigList::find_key_word(const char *key) const 
00044 {
00045  ConfigListNode *tmp;
00046  for (tmp=theList; tmp!= NULL; tmp = tmp -> next) {
00047    if (!strcasecmp(tmp->name, key))
00048      return tmp;
00049  }
00050  return NULL;
00051 }
00052 
00053 //  I have a new name/ data pair.  If the name does not already exist,
00054 // make a new ListData element and put it on the HEAD of theList.  If
00055 // name does already exist, make a new ListNode and add the name to the end
00056 //   String 1 starts at s1 and is of length len1
00057 //      "   2    "   "  s2  "   "  "   "    len2
00058 void ConfigList::add_element(const char *s1, int len1, const char *s2, int len2)
00059 {
00060     char *temps = new char[len1 + 1];  // what is the name?
00061     if ( temps == NULL )
00062     {
00063       NAMD_die("new failed in ConfigList::add_element");
00064     }
00065 //printf("%s %s\n",s1,s2);
00066 #ifndef NAMD_NO_STDOUT_FLUSH
00067     fflush(stdout);
00068 #endif
00069     strncpy(temps, s1, len1);
00070     temps[len1] = 0;                   //       terminate the string
00071     ConfigListNode *tmpList;
00072                                        
00073     tmpList = find_key_word( temps);  // go through the list
00074     if (tmpList == NULL )  {          // if not found
00075        tmpList = new ConfigListNode( theList, temps, NULL);// create a new node
00076        if ( tmpList == NULL )
00077        {
00078          NAMD_die("new failed in ConfigList::add_element");
00079        }
00080                                     // and stick it on the head of the list
00081        theList = tmpList;           // note that I can continue to use tmpList
00082     }
00083 
00084     if (len1 < len2) {                  // if string is smaller, don't realloc
00085       delete [] temps;
00086       temps = new char[len2 + 1];
00087       if ( temps == NULL )
00088       {
00089         NAMD_die("new failed in ConfigList::add_element");
00090       }
00091     }
00092     strncpy(temps, s2, len2);           // get the new string
00093     temps[len2] = 0;
00094 
00095     StringList *newStrList = new StringList(temps);  // new element
00096     if ( newStrList == NULL )
00097     {
00098       NAMD_die("new failed in ConfigList::add_element");
00099     }
00100     newStrList -> next = NULL;
00101     if (tmpList -> data == NULL) {       // no previous definition
00102       tmpList -> data = newStrList;      //    so this is the new head
00103     } else {
00104       StringList *tmpStrList = tmpList -> data;   // else,
00105       while (tmpStrList -> next != NULL)          // find the end of the list
00106         tmpStrList = tmpStrList -> next;
00107       tmpStrList -> next = newStrList;            // and stick it on the tail
00108     }
00109     
00110     delete [] temps;
00111 }
00112 
00113 
00114 // The scripting interface will call our add_element routine.
00115 
00116 ConfigList::ConfigList(void)
00117 {
00118   isokay = TRUE;
00119   theList = NULL;
00120 }
00121 
00122 // Used to implement "source" for file parser below.
00123 
00124 struct FileStack {
00125   FILE *file;
00126   int linenumber;
00127   char *filename;
00128   FileStack *next;
00129 };
00130 
00131 // open, read, parse, and close the file
00132 // make a linked list of "AssocList"
00133 // The file is parsed as (I think):
00134 // [w]?([W])*[w]?[=]?[w]?([W][wW])?[w]
00135 // where w == white space; W is non-whitespace
00136 // [x]? says that there can be 0 or more characters of type x.
00137 // [x]* says there must be at least 1 character of type x
00138 // the terms in () are the two that are used.  Everything after a '#'
00139 // sign is excluded.  I'm not sure how to specify that exclusion in
00140 // my regular expression
00141 
00142 // a new one:  if the first character of the data is '{'
00143 //   then I append new keyword values for each line until I get to
00144 //   a line with the first non-blank character as a '}'
00145 ConfigList::ConfigList(const char *filename_in)
00146 {
00147   char *filename = new char[strlen(filename_in)+1];
00148   strcpy(filename,filename_in);
00149   FileStack *fileStack = 0;
00150   FILE *infile;
00151   
00152   isokay = FALSE;
00153   theList = NULL;
00154   int linenumber = 0;   // keep track of line numbers for searching out errors
00155 
00156   if (!strcmp(filename,"-")) {  // should the input be from stdin?
00157     infile = stdin;
00158   } else {
00159     if ( (infile = Fopen(filename, "r")) == NULL ) {
00160         iout << iWARN << "Unable to open configuration file '" 
00161                  << filename << "'.\n" << endi;
00162         isokay = FALSE;
00163         return;
00164     }
00165   }
00166   isokay = TRUE;         // file is now open
00167 
00168      // so read and parse it
00169   char buf[1000]; // give myself lots of space
00170   char *namestart, *nameend, *datastart, *dataend;
00171   char *s;
00172   int spacecount;
00173   int fileok;
00174   while ((fileok = ! ! fgets(buf, 999, infile)) || fileStack) {
00175     if ( fileStack && ! fileok ) { // done with "source"
00176       delete [] filename;
00177       filename = fileStack->filename;
00178       linenumber = fileStack->linenumber;
00179       infile = fileStack->file;
00180       FileStack *delStack = fileStack;
00181       fileStack = fileStack->next;
00182       delete delStack;
00183       continue;
00184     }
00185     linenumber ++;        
00186     namestart = nameend = datastart = dataend = NULL;
00187     spacecount = 0;
00188     
00189     for (s = buf; *s && *s!='\n'; s++) {    // get to the end of the line
00190        if (*s == '#')                       // found a comment, so break
00191           break;
00192        if ( !isspace(*s) )    // dataend will always be the last non-blank char
00193           dataend = s;
00194        if ( !isspace(*s) && !namestart)     // found first character of name
00195           {namestart = s; continue; }
00196        if ( (isspace(*s)  || *s == '=') &&  // found last character of name
00197                  namestart && !nameend)
00198           nameend = s - 1;
00199        if ( !isspace(*s) && !datastart &&   // found the next char. after name
00200                  nameend)
00201           if (*s == '=' && spacecount == 0) // an equals is allowed
00202              {spacecount++; continue; }     // but only once
00203             else
00204              {datastart = s; continue; }    // otherwise, use it
00205     }
00206     if (*s == '\n')          // cut out the newline at the end
00207       *s = 0;
00208      else
00209       if (*s != '#') {       // then there was an overflow
00210         iout << iWARN << "Line " << linenumber << " of configuration file "
00211                  << filename << " contains more than 999 characters."
00212                  << "  Excess characters will be ignored.\n" << endi;
00213       } else {
00214         *s = 0;  // delete the '#' character
00215       }
00216 
00217 // I will also ignore line that I can't understand
00218 // If there is any text on the line (as compared to a blank or commented)
00219 //   line, then I will say that there is a problem.
00220     if (!namestart || !nameend || !datastart || !dataend) {
00221       if (!namestart && datastart || namestart && !datastart) {// was some data
00222         iout << iWARN << "Couldn't parse line " << linenumber << " in "
00223                  << "configuration file " << filename << ".  The line was: "
00224                  << buf << "\n" << endi;
00225       }
00226       continue;  // which ever the case, go to the next line
00227     }
00228 
00229    if ( ! strncmp(namestart, "source", nameend-namestart+1) )  {
00230      // see if the the name is "source"
00231 
00232      // store the old file data
00233      FileStack *newStack = new FileStack;
00234      newStack->filename = filename;
00235      newStack->linenumber = linenumber;
00236      newStack->file = infile;
00237      newStack->next = fileStack;
00238      fileStack = newStack;
00239 
00240      // copy the filename
00241      char *cpychar = new char[dataend-datastart+2];
00242      strcpy(cpychar,datastart);
00243      filename = cpychar;
00244 
00245      // open the sourced file
00246      if ( (infile = Fopen(filename, "r")) == NULL ) {
00247         iout << iWARN << "Unable to open file '" 
00248                  << filename << "' sourced by '"
00249                 << fileStack->filename << "' at line "
00250                 << fileStack->linenumber << ".\n" << endi;
00251         isokay = FALSE;
00252         return;
00253      }
00254      iout << iINFO << "Sourcing " << filename << "\n" << endi;
00255      isokay = TRUE;         // file is now open
00256      linenumber = 0;
00257      
00258    } else if (datastart[0] == '{') {
00259      // check if the data begins with a '{'
00260      // will remove initial '{' and final '}' to match Tcl.
00261      std::ostringstream alldata;
00262      char newdata[1000];
00263      int found_end = 0;
00264      ++datastart;  // remove initial '{'
00265      int open_brace_count = 1;
00266      char *newline = datastart;
00267      strcat(newline,"\n");  // put back newline that was removed above
00268 
00269      while ( 1 ) {
00270        int i;
00271        int escape_next = 0;
00272        for (i=0; i<1000; i++) {
00273          if (! newline[i]) {
00274            break;
00275          }
00276          if (escape_next) {
00277            escape_next = 0;
00278            continue;
00279          }
00280          if (newline[i] == '\\' && ! escape_next) {
00281            escape_next = 1;
00282          }
00283          if (newline[i] == '{' && ! escape_next) {
00284            ++open_brace_count;
00285          }
00286          if (newline[i] == '}' && ! escape_next) {
00287            if ( ( found_end = ! --open_brace_count ) ) {
00288              newline[i] = 0;
00289              break;
00290            }
00291          }
00292        }
00293        alldata << newline;
00294        if (found_end) break;
00295        newline = newdata;
00296        if ( ! fgets(newdata, 999, infile) ) break;
00297        linenumber ++;
00298      }
00299      std::string alldatastr = alldata.str();
00300      add_element(namestart, nameend-namestart+1, alldatastr.c_str(), alldata.str().length());
00301      // delete string?
00302      if (!found_end) {
00303        *(nameend+1) = 0;
00304        sprintf(newdata, "configuration file ended early while parsing line "
00305                "%d of keyword structure %s", linenumber, namestart);
00306        NAMD_die(newdata);
00307      }
00308    } else {
00309      // now I can add the new values to the linked list
00310      add_element( namestart, nameend - namestart + 1, datastart,
00311                   dataend - datastart + 1 );
00312    }
00313   } // while I can still get data with fgets
00314   
00315   if (strcmp(filename,"-")) {  // close the input file if not stdin
00316     Fclose(infile);
00317   }
00318   delete [] filename;
00319 }
00320 
00321 // destructor for the class - just delete a linked list
00322 ConfigList::~ConfigList( void) 
00323 {
00324   ConfigListNode *curr=theList, *next=NULL;
00325 
00326   while ( curr!=NULL ) 
00327   {
00328      next = curr -> next;
00329      delete curr;     // the nasties for deleted the linked list of string
00330                       // has already been defined in the typedef struct
00331                       // for ConfigListNode
00332      curr = next;
00333   }
00334 
00335   theList = NULL;
00336 } // all done
00337 
00338 
00339 // Given "name", find all the elements of the ConfigList that
00340 //  have have that "name"
00341 StringList *ConfigList::find(const char *name) const
00342 {
00343   ConfigListNode *tmpList;
00344   tmpList = find_key_word(name);
00345   if (tmpList != NULL)
00346     return tmpList -> data;
00347   return NULL;
00348 }
00349 
00350 //#define TEST_CONFIGLIST_C
00351 #ifdef TEST_CONFIGLIST_C
00352 #include <iostream.h>
00353 
00354 main()
00355 {
00356   ConfigList dat("namd.conf"); // an example file
00357   StringList *strings;
00358   int i;
00359   
00360   if (!dat.okay())
00361     NAMD_die("Can't continue - cannot get info from file.");
00362   std::cout << "Searching for: 'fullname'\n";
00363   strings = dat.find("fullname");
00364   if (!strings) {
00365     NAMD_die("Couldn't find fullname.\n");
00366   }
00367   i=0;
00368   while (strings) {   // write all the data associated with 'fullname'
00369     std::cout << i << " = " << strings->data << '\n';
00370     strings = strings -> next;
00371     i++;
00372   }
00373 }
00374 
00375 #endif
00376 

Generated on Thu Nov 23 01:17:12 2017 for NAMD by  doxygen 1.4.7