$search
00001 /* 00002 * Copyright (c) 2011, Willow Garage, Inc. 00003 * All rights reserved. 00004 * 00005 * Redistribution and use in source and binary forms, with or without 00006 * modification, are permitted provided that the following conditions are met: 00007 * 00008 * * Redistributions of source code must retain the above copyright 00009 * notice, this list of conditions and the following disclaimer. 00010 * * Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * * Neither the name of the Willow Garage, Inc. nor the names of its 00014 * contributors may be used to endorse or promote products derived from 00015 * this software without specific prior written permission. 00016 * 00017 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00018 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00019 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00020 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 00021 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00022 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00023 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00024 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00025 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00026 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00027 * POSSIBILITY OF SUCH DAMAGE. 00028 */ 00029 00030 #include "rviz/config.h" 00031 00032 #include <fstream> 00033 #include <sstream> 00034 00035 namespace rviz 00036 { 00037 00038 bool Config::readFromFile( const std::string& filename ) 00039 { 00040 std::ifstream in( filename.c_str() ); 00041 if( in ) 00042 { 00043 read( in ); 00044 return true; 00045 } 00046 else 00047 { 00048 std::cerr << "Config file '" << filename << "' could not be opened for reading." << std::endl; 00049 return false; 00050 } 00051 } 00052 00053 bool Config::writeToFile( const std::string& filename ) 00054 { 00055 std::ofstream out( filename.c_str() ); 00056 if( out ) 00057 { 00058 write( out ); 00059 return true; 00060 } 00061 else 00062 { 00063 std::cerr << "Config file '" << filename << "' could not be opened for writing." << std::endl; 00064 return false; 00065 } 00066 } 00067 00068 void Config::read( std::istream& input ) 00069 { 00070 size_t equals_sign_index; 00071 std::string line; 00072 std::string current_dir; 00073 std::string key, value; 00074 00075 // Loop over all lines. 00076 while( !input.eof() && !input.fail() ) 00077 { 00078 // Read the next line. 00079 line.clear(); 00080 std::getline( input, line ); 00081 00082 if( line.size() > 0 ) // Ignore empty lines. 00083 { 00084 if( line[0] == '[' ) // Keep track of the current "directory" to prepend to the keys. 00085 { 00086 current_dir = line.substr( 1, line.size() - 2 ); 00087 } 00088 else 00089 { 00090 // Parse a key=value line. 00091 equals_sign_index = line.find_first_of( '=' ); 00092 key = line.substr( 0, equals_sign_index ); 00093 key = unescapeKey( key ); 00094 value = line.substr( equals_sign_index + 1 ); 00095 00096 // Store the key,value pair if the key is not empty. 00097 if( key.size() > 0 ) 00098 { 00099 if( current_dir.size() > 0 ) 00100 { 00101 key = current_dir + '/' + key; 00102 } 00103 set( key, value ); 00104 } 00105 } 00106 } 00107 } 00108 } 00109 00110 void Config::write( std::ostream& output ) 00111 { 00112 std::string last_prefix; 00113 std::string key_tail; 00114 std::string key_prefix; 00115 00116 for( Iterator it = begin(); it != end(); it++ ) 00117 { 00118 const std::string& key = (*it).first; 00119 const std::string& value = (*it).second; 00120 size_t last_slash_index = key.find_last_of( '/' ); 00121 if( last_slash_index == std::string::npos ) 00122 { 00123 key_tail = key; 00124 key_prefix = ""; 00125 } 00126 else 00127 { 00128 key_tail = key.substr( last_slash_index + 1 ); 00129 key_prefix = key.substr( 0, last_slash_index ); 00130 } 00131 if( key_prefix != last_prefix ) 00132 { 00133 writeDirectory( output, key_prefix, last_prefix ); 00134 } 00135 key_tail = escapeKey( key_tail ); 00136 output << key_tail << "=" << value << std::endl; 00137 last_prefix = key_prefix; 00138 } 00139 } 00140 00141 // Write a directory description of new_dir to output, given that 00142 // we were previously "in" prev_dir. For example: 00143 // 00144 // new_dir = a/c, prev_dir = a/b/z, output: 00145 // [a/c] 00146 // 00147 // new_dir = a/c, prev_dir = a, output: 00148 // [a/c] 00149 // 00150 // new_dir = a/c, prev_dir = "", output: 00151 // [a] 00152 // [a/c] 00153 // 00154 // new_dir = a/c, prev_dir = "b", output: 00155 // [a] 00156 // [a/c] 00157 // 00158 // new_dir = a/b/c, prev_dir = "a", output: 00159 // [a/b] 00160 // [a/b/c] 00161 // 00162 // new_dir = a/b/c, prev_dir = "a/z/y", output: 00163 // [a/b] 00164 // [a/b/c] 00165 void Config::writeDirectory( std::ostream& output, const std::string& new_dir, const std::string& prev_dir ) 00166 { 00167 // Find common initial substring between new_dir and prev_dir 00168 size_t min_size = new_dir.size() < prev_dir.size() ? new_dir.size() : prev_dir.size(); 00169 00170 size_t index = 0; // index of first non-matching char. 00171 for( ; index < min_size; index++ ) 00172 { 00173 if( new_dir[ index ] != prev_dir[ index ] ) 00174 { 00175 break; 00176 } 00177 } 00178 00179 // If we are pointed at a '/' move just past it. 00180 if( index < new_dir.size() && new_dir[ index ] == '/' ) 00181 { 00182 index++; 00183 } 00184 00185 // Search forward for first '/' after the matching part. That will 00186 // be the end of the first string we need to print. 00187 bool done = false; 00188 while( !done ) 00189 { 00190 index = new_dir.find_first_of( '/', index ); 00191 if( index == std::string::npos ) 00192 { 00193 index = new_dir.size(); 00194 done = true; 00195 } 00196 output << '[' << new_dir.substr( 0, index ) << ']' << std::endl; 00197 index++; // Skip the '/' 00198 } 00199 } 00200 00201 std::string Config::escapeKey( const std::string& raw_key ) 00202 { 00203 std::istringstream in( raw_key ); 00204 std::ostringstream out; 00205 char c; 00206 while( in.good() ) 00207 { 00208 c = in.get(); 00209 if( in ) 00210 { 00211 switch( c ) 00212 { 00213 case ':': 00214 case ' ': 00215 case '\\': 00216 out << '\\'; 00217 } 00218 out << c; 00219 } 00220 } 00221 return out.str(); 00222 } 00223 00224 std::string Config::unescapeKey( const std::string& cooked_key ) 00225 { 00226 std::istringstream in( cooked_key ); 00227 std::ostringstream out; 00228 char c; 00229 while( in.good() ) 00230 { 00231 c = in.get(); 00232 if( in.good() ) 00233 { 00234 if( c == '\\' ) 00235 { 00236 c = in.get(); 00237 if( in.good() ) 00238 { 00239 out << c; 00240 } 00241 } 00242 else 00243 { 00244 out << c; 00245 } 00246 } 00247 } 00248 return out.str(); 00249 } 00250 00251 void Config::set( const std::string& key, float value ) 00252 { 00253 std::stringstream ss; 00254 ss << value; 00255 map_[ stripFirstSlash( key )] = ss.str(); 00256 } 00257 00258 void Config::set( const std::string& key, int value ) 00259 { 00260 std::stringstream ss; 00261 ss << value; 00262 map_[ stripFirstSlash( key )] = ss.str(); 00263 } 00264 00265 bool Config::get( const std::string& key, std::string* output, const std::string& default_value ) 00266 { 00267 Iterator it = map_.find( stripFirstSlash( key )); 00268 if( it != map_.end() ) 00269 { 00270 *output = (*it).second; 00271 return true; 00272 } 00273 *output = default_value; 00274 return false; 00275 } 00276 00277 bool Config::get( const std::string& key, float* output, float default_value ) 00278 { 00279 Iterator it = map_.find( stripFirstSlash( key )); 00280 if( it != map_.end() ) 00281 { 00282 std::istringstream iss; 00283 iss.str( (*it).second ); 00284 iss >> *output; 00285 if( !iss.fail() ) 00286 { 00287 return true; 00288 } 00289 } 00290 *output = default_value; 00291 return false; 00292 } 00293 00294 bool Config::get( const std::string& key, int* output, int default_value ) 00295 { 00296 Iterator it = map_.find( stripFirstSlash( key )); 00297 if( it != map_.end() ) 00298 { 00299 std::istringstream iss; 00300 iss.str( (*it).second ); 00301 iss >> *output; 00302 if( !iss.fail() ) 00303 { 00304 return true; 00305 } 00306 } 00307 *output = default_value; 00308 return false; 00309 } 00310 00311 const std::string Config::stripFirstSlash( const std::string& str ) 00312 { 00313 if( str[0] == '/' ) 00314 { 00315 return str.substr( 1 ); 00316 } 00317 else 00318 { 00319 return str; 00320 } 00321 } 00322 00323 bool Config::DirectoryCompare::operator() (const std::string& lhs, const std::string& rhs) const 00324 { 00325 int start = 0; 00326 int count; 00327 int rhs_count; 00328 size_t l_slash_index, r_slash_index; 00329 bool l_on_last, r_on_last; 00330 00331 // Loop once for each section of the strings (where sections are 00332 // delimited by '/' characters) as long as the corresponding 00333 // sections are equal. When we come to a section in which lhs != 00334 // rhs, return either true or false. 00335 while( true ) 00336 { 00337 // Find the index of the next slash in each string, if there is one. 00338 l_slash_index = lhs.find_first_of( '/', start ); 00339 r_slash_index = rhs.find_first_of( '/', start ); 00340 l_on_last = ( l_slash_index == std::string::npos ); 00341 if( l_on_last ) 00342 { 00343 l_slash_index = lhs.size(); 00344 } 00345 r_on_last = ( r_slash_index == std::string::npos ); 00346 if( r_on_last ) 00347 { 00348 r_slash_index = rhs.size(); 00349 } 00350 00351 // If one but not both of the strings is on the last section, we 00352 // know the result. A string which is on its last element is 00353 // "less" than a string which has more elements coming, regardless 00354 // of the current-element comparison. 00355 if( !l_on_last && r_on_last ) 00356 { 00357 return false; 00358 } 00359 if( l_on_last && !r_on_last ) 00360 { 00361 return true; 00362 } 00363 00364 // Update the comparison lengths. 00365 count = l_slash_index - start; 00366 rhs_count = r_slash_index - start; 00367 00368 // Compare the current section of each string. 00369 int result = lhs.compare( start, count, rhs, start, rhs_count ); 00370 00371 // If the sections differ, return true or false according to the 00372 // direction of difference. 00373 if( result != 0 ) 00374 { 00375 return result < 0; 00376 } 00377 00378 // The sections are equal, so if the rhs is ending, then the rhs 00379 // is less, so return false. 00380 if( start + rhs_count >= (int)rhs.size() ) 00381 { 00382 return false; 00383 } 00384 00385 // Move start index to the next section, skipping over the '/' 00386 // character with the +1. 00387 start += count + 1; 00388 00389 // The sections are equal, so if the lhs is ending, it is less, so 00390 // return true. 00391 if( start > (int)lhs.size() ) 00392 { 00393 return true; 00394 } 00395 } 00396 } 00397 00398 } // end namespace rviz