IBR-DTNSuite  0.10
ConfigFile.cpp
Go to the documentation of this file.
1 /*
2  * ConfigFile.cpp
3  *
4  * Class for reading named values from configuration files
5  * Richard J. Wagner v2.1 24 May 2004 wagnerr@umich.edu
6  *
7  * Copyright (c) 2004 Richard J. Wagner
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to
11  * deal in the Software without restriction, including without limitation the
12  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
13  * sell copies of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25  * IN THE SOFTWARE.
26  *
27  */
28 
29 #include "ibrcommon/config.h"
31 
32 namespace ibrcommon
33 {
34  ConfigFile::ConfigFile( string filename, string delimiter,
35  string comment, string sentry )
36  : myDelimiter(delimiter), myComment(comment), mySentry(sentry)
37  {
38  // Construct a ConfigFile, getting keys and values from given file
39 
40  std::ifstream in( filename.c_str() );
41 
42  if( !in ) throw file_not_found( filename );
43 
44  in >> (*this);
45  }
46 
47 
49  : myDelimiter( string(1,'=') ), myComment( string(1,'#') )
50  {
51  // Construct a ConfigFile without a file; empty
52  }
53 
54 
55  void ConfigFile::remove( const string& key )
56  {
57  // Remove key and its value
58  myContents.erase( myContents.find( key ) );
59  return;
60  }
61 
62 
63  bool ConfigFile::keyExists( const string& key ) const
64  {
65  // Indicate whether key is found
66  mapci p = myContents.find( key );
67  return ( p != myContents.end() );
68  }
69 
70 
71  /* static */
72  void ConfigFile::trim( string& s )
73  {
74  // Remove leading and trailing whitespace
75  static const char whitespace[] = " \n\t\v\r\f";
76  s.erase( 0, s.find_first_not_of(whitespace) );
77  s.erase( s.find_last_not_of(whitespace) + 1U );
78  }
79 
80 
81  std::ostream& operator<<( std::ostream& os, const ConfigFile& cf )
82  {
83  // Save a ConfigFile to os
84  for( ConfigFile::mapci p = cf.myContents.begin();
85  p != cf.myContents.end();
86  ++p )
87  {
88  os << p->first << " " << cf.myDelimiter << " ";
89  os << p->second << std::endl;
90  }
91  return os;
92  }
93 
94 
95  std::istream& operator>>( std::istream& is, ConfigFile& cf )
96  {
97  // Load a ConfigFile from is
98  // Read in keys and values, keeping internal whitespace
99  typedef string::size_type pos;
100  const string& delim = cf.myDelimiter; // separator
101  const string& comm = cf.myComment; // comment
102  const string& sentry = cf.mySentry; // end of file sentry
103  const pos skip = delim.length(); // length of separator
104 
105  string nextline = ""; // might need to read ahead to see where value ends
106 
107  while( is || nextline.length() > 0 )
108  {
109  // Read an entire line at a time
110  string line;
111  if( nextline.length() > 0 )
112  {
113  line = nextline; // we read ahead; use it now
114  nextline = "";
115  }
116  else
117  {
118  std::getline( is, line );
119  }
120 
121  // Ignore comments
122  line = line.substr( 0, line.find(comm) );
123 
124  // Check for end of file sentry
125  if( sentry != "" && line.find(sentry) != string::npos ) return is;
126 
127  // Parse the line if it contains a delimiter
128  pos delimPos = line.find( delim );
129  if( delimPos < string::npos )
130  {
131  // Extract the key
132  string key = line.substr( 0, delimPos );
133  line.replace( 0, delimPos+skip, "" );
134 
135  // See if value continues on the next line
136  // Stop at blank line, next line with a key, end of stream,
137  // or end of file sentry
138  bool terminate = false;
139  while( !terminate && is )
140  {
141  std::getline( is, nextline );
142  terminate = true;
143 
144  string nlcopy = nextline;
145  ConfigFile::trim(nlcopy);
146  if( nlcopy == "" ) continue;
147 
148  nextline = nextline.substr( 0, nextline.find(comm) );
149  if( nextline.find(delim) != string::npos )
150  continue;
151  if( sentry != "" && nextline.find(sentry) != string::npos )
152  continue;
153 
154  nlcopy = nextline;
155  ConfigFile::trim(nlcopy);
156  if( nlcopy != "" ) line += "\n";
157  line += nextline;
158  terminate = false;
159  }
160 
161  // Store key and value
162  ConfigFile::trim(key);
163  ConfigFile::trim(line);
164  cf.myContents[key] = line; // overwrites if key is repeated
165  }
166  }
167 
168  return is;
169  }
170 }