1 // ConfigFile.cpp
2 
3 #include "ConfigFile.h"
4 
5 using std::string;
6 
ConfigFile(string filename,string delimiter,string comment,string sentry)7 ConfigFile::ConfigFile( string filename, string delimiter,
8                         string comment, string sentry )
9 	: myDelimiter(delimiter), myComment(comment), mySentry(sentry)
10 {
11 	// Construct a ConfigFile, getting keys and values from given file
12 
13 	std::ifstream in( filename.c_str() );
14 
15 	if( !in ) throw file_not_found( filename );
16 
17 	in >> (*this);
18 }
19 
20 
ConfigFile()21 ConfigFile::ConfigFile()
22 	: myDelimiter( string(1,'=') ), myComment( string(1,'#') )
23 {
24 	// Construct a ConfigFile without a file; empty
25 }
26 
27 
remove(const string & key)28 void ConfigFile::remove( const string& key )
29 {
30 	// Remove key and its value
31 	myContents.erase( myContents.find( key ) );
32 	return;
33 }
34 
35 
keyExists(const string & key) const36 bool ConfigFile::keyExists( const string& key ) const
37 {
38 	// Indicate whether key is found
39 	mapci p = myContents.find( key );
40 	return ( p != myContents.end() );
41 }
42 
43 
44 /* static */
trim(string & s)45 void ConfigFile::trim( string& s )
46 {
47 	// Remove leading and trailing whitespace
48 	static const char whitespace[] = " \n\t\v\r\f";
49 	s.erase( 0, s.find_first_not_of(whitespace) );
50 	s.erase( s.find_last_not_of(whitespace) + 1U );
51 }
52 
53 
operator <<(std::ostream & os,const ConfigFile & cf)54 std::ostream& operator<<( std::ostream& os, const ConfigFile& cf )
55 {
56 	// Save a ConfigFile to os
57 	for( ConfigFile::mapci p = cf.myContents.begin();
58 	     p != cf.myContents.end();
59 		 ++p )
60 	{
61 		os << p->first << " " << cf.myDelimiter << " ";
62 		os << p->second << std::endl;
63 	}
64 	return os;
65 }
66 
67 
operator >>(std::istream & is,ConfigFile & cf)68 std::istream& operator>>( std::istream& is, ConfigFile& cf )
69 {
70 	// Load a ConfigFile from is
71 	// Read in keys and values, keeping internal whitespace
72 	typedef string::size_type pos;
73 	const string& delim  = cf.myDelimiter;  // separator
74 	const string& comm   = cf.myComment;    // comment
75 	const string& sentry = cf.mySentry;     // end of file sentry
76 	const pos skip = delim.length();        // length of separator
77 
78 	string nextline = "";  // might need to read ahead to see where value ends
79 
80 	while( is || nextline.length() > 0 )
81 	{
82 		// Read an entire line at a time
83 		string line;
84 		if( nextline.length() > 0 )
85 		{
86 			line = nextline;  // we read ahead; use it now
87 			nextline = "";
88 		}
89 		else
90 		{
91 			std::getline( is, line );
92 		}
93 
94 		// Ignore comments
95 		line = line.substr( 0, line.find(comm) );
96 
97 		// Check for end of file sentry
98 		if( sentry != "" && line.find(sentry) != string::npos ) return is;
99 
100 		// Parse the line if it contains a delimiter
101 		pos delimPos = line.find( delim );
102 		if( delimPos < string::npos )
103 		{
104 			// Extract the key
105 			string key = line.substr( 0, delimPos );
106 			line.replace( 0, delimPos+skip, "" );
107 
108 			// See if value continues on the next line
109 			// Stop at blank line, next line with a key, end of stream,
110 			// or end of file sentry
111 			bool terminate = false;
112 			while( !terminate && is )
113 			{
114 				std::getline( is, nextline );
115 				terminate = true;
116 
117 				string nlcopy = nextline;
118 				ConfigFile::trim(nlcopy);
119 				if( nlcopy == "" ) continue;
120 
121 				nextline = nextline.substr( 0, nextline.find(comm) );
122 				if( nextline.find(delim) != string::npos )
123 					continue;
124 				if( sentry != "" && nextline.find(sentry) != string::npos )
125 					continue;
126 
127 				nlcopy = nextline;
128 				ConfigFile::trim(nlcopy);
129 				if( nlcopy != "" ) line += "\n";
130 				line += nextline;
131 				terminate = false;
132 			}
133 
134 			// Store key and value
135 			ConfigFile::trim(key);
136 			ConfigFile::trim(line);
137 			cf.myContents[key] = line;  // overwrites if key is repeated
138 		}
139 	}
140 
141 	return is;
142 }
143