📄 config_file_iterator.cpp
字号:
// (C) Copyright Gennadiy Rozental 2005.
// Use, modification, and distribution are subject to the
// Boost Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/libs/test for the library home page.
//
// File : $RCSfile: config_file_iterator.cpp,v $
//
// Version : $Revision: 1.1 $
//
// Description : flexible configuration file iterator implementation
// ***************************************************************************
// Boost.Runtime.Parameter
#include <boost/test/utils/runtime/config.hpp>
#include <boost/test/utils/runtime/file/config_file_iterator.hpp>
#include <boost/test/utils/runtime/validation.hpp>
#include <boost/test/utils/runtime/env/environment.hpp>
// Boost
#include <boost/utility.hpp>
#include <boost/scoped_array.hpp>
#include <boost/bind.hpp>
// Boost.Test
#include <boost/test/utils/basic_cstring/compare.hpp>
#include <boost/test/utils/algorithm.hpp>
#include <boost/test/utils/iterator/token_iterator.hpp>
#include <boost/test/utils/assign_op.hpp>
// STL
#include <memory>
#include <map>
#include <list>
#include <vector>
#include <fstream>
#include <cctype>
#include <iostream>
namespace boost {
namespace BOOST_RT_PARAM_NAMESPACE {
namespace file {
// ************************************************************************** //
// ************** symbol_to_value_map ************** //
// ************************************************************************** //
template<typename ValueType>
struct symbol_to_value_map : std::map<cstring, ValueType> {
template<typename ParamType>
void add( cstring name, ParamType const& value )
{
using namespace unit_test;
m_name_store.push_back( dstring() );
assign_op( m_name_store.back(), name, 0 );
assign_op( (*this)[m_name_store.back()], value, 0 );
}
void remove( cstring name )
{
std::list<dstring>::iterator it = std::find( m_name_store.begin(), m_name_store.end(), name );
m_name_store.erase( it );
erase( name );
}
private:
std::list<dstring> m_name_store;
};
// ************************************************************************** //
// ************** symbol_table_t ************** //
// ************************************************************************** //
typedef symbol_to_value_map<dstring> symbol_table_t;
// ************************************************************************** //
// ************** command_handler_map ************** //
// ************************************************************************** //
typedef symbol_to_value_map<config_file_iterator::command_handler> command_handler_map;
// ************************************************************************** //
// ************** is_valid_identifier ************** //
// ************************************************************************** //
static bool
is_valid_identifier( cstring const& source )
{
if( source.is_empty() )
return false;
cstring::const_iterator it = source.begin();
if( !std::isalpha( *it ) )
return false;
while( ++it < source.end() ) {
if( !std::isalnum( *it ) && *it != BOOST_RT_PARAM_LITERAL( '_' ) && *it != BOOST_RT_PARAM_LITERAL( '-' ) )
return false;
}
return true;
}
// ************************************************************************** //
// ************** include_level ************** //
// ************************************************************************** //
struct include_level;
typedef std::auto_ptr<include_level> include_level_ptr;
struct include_level : noncopyable
{
// Constructor
explicit include_level( cstring file_name, cstring path_separators, include_level* parent = 0 );
// Data members
std::ifstream m_stream;
location m_curr_location;
include_level_ptr m_parent;
};
//____________________________________________________________________________//
include_level::include_level( cstring file_name, cstring path_separators, include_level* parent_ )
: m_parent( parent_ )
{
if( file_name.is_empty() )
return;
assign_op( m_curr_location.first, file_name, 0 );
m_curr_location.second = 0;
m_stream.open( m_curr_location.first.c_str() );
if( !m_stream.is_open() && !!m_parent.get() ) {
cstring parent_path = m_parent->m_curr_location.first;
cstring::iterator it = unit_test::find_last_of( parent_path.begin(), parent_path.end(),
path_separators.begin(),
path_separators.end() );
if( it != parent_path.end() ) {
assign_op( m_curr_location.first, cstring( parent_path.begin(), it+1 ), 0 );
m_curr_location.first.append( file_name.begin(), file_name.end() );
m_stream.clear();
m_stream.open( m_curr_location.first.c_str() );
}
}
BOOST_RT_PARAM_VALIDATE_LOGIC( m_stream.is_open(), BOOST_RT_PARAM_LITERAL( "couldn't open file " ) << file_name );
}
//____________________________________________________________________________//
// ************************************************************************** //
// ************** config_file_iterator::Impl ************** //
// ************************************************************************** //
struct config_file_iterator::Impl : noncopyable {
// Constructor
Impl();
bool get_next_line( cstring& next_line );
void process_command_line( cstring line );
void process_include( cstring line );
void process_define( cstring line );
void process_undef( cstring line );
void process_ifdef( cstring line );
void process_ifndef( cstring line );
void process_else( cstring line );
void process_endif( cstring line );
boost::optional<cstring>
get_macro_value( cstring macro_name, bool ignore_missing = true );
void substitute_macros( cstring& where );
bool is_active_line() { return m_inactive_ifdef_level == 0; }
static bool match_front( cstring str, cstring pattern )
{
return str.size() >= pattern.size() && str.substr( 0, pattern.size() ) == pattern;
}
static bool match_back( cstring str, cstring pattern )
{
return str.size() >= pattern.size() && str.substr( str.size() - pattern.size() ) == pattern;
}
// Configurable parameters
dstring m_path_separators;
char_type m_line_delimeter;
dstring m_sl_comment_delimeter;
dstring m_command_delimeter;
dstring m_line_beak;
dstring m_macro_ref_begin;
dstring m_macro_ref_end;
dstring m_include_kw;
dstring m_define_kw;
dstring m_undef_kw;
dstring m_ifdef_kw;
dstring m_ifndef_kw;
dstring m_else_kw;
dstring m_endif_kw;
std::size_t m_buffer_size;
bool m_trim_trailing_spaces;
bool m_trim_leading_spaces;
bool m_skip_empty_lines;
bool m_detect_missing_macro;
// Data members
dstring m_post_subst_line;
scoped_array<char> m_buffer;
include_level_ptr m_curr_level;
symbol_table_t m_symbols_table;
std::vector<bool> m_conditional_states;
std::size_t m_inactive_ifdef_level;
command_handler_map m_command_handler_map;
};
//____________________________________________________________________________//
config_file_iterator::Impl::Impl()
: m_path_separators( BOOST_RT_PARAM_LITERAL( "/\\" ) )
, m_line_delimeter( BOOST_RT_PARAM_LITERAL( '\n' ) )
, m_sl_comment_delimeter( BOOST_RT_PARAM_LITERAL( "#" ) )
, m_command_delimeter( BOOST_RT_PARAM_LITERAL( "$" ) )
, m_line_beak( BOOST_RT_PARAM_LITERAL( "\\" ) )
, m_macro_ref_begin( BOOST_RT_PARAM_LITERAL( "$" ) )
, m_macro_ref_end( BOOST_RT_PARAM_LITERAL( "$" ) )
, m_include_kw( BOOST_RT_PARAM_LITERAL( "include" ) )
, m_define_kw( BOOST_RT_PARAM_LITERAL( "define" ) )
, m_undef_kw( BOOST_RT_PARAM_LITERAL( "undef" ) )
, m_ifdef_kw( BOOST_RT_PARAM_LITERAL( "ifdef" ) )
, m_ifndef_kw( BOOST_RT_PARAM_LITERAL( "ifndef" ) )
, m_else_kw( BOOST_RT_PARAM_LITERAL( "else" ) )
, m_endif_kw( BOOST_RT_PARAM_LITERAL( "endif" ) )
, m_buffer_size( 8192 )
, m_trim_trailing_spaces( true )
, m_trim_leading_spaces( false )
, m_skip_empty_lines( true )
, m_detect_missing_macro( true )
, m_inactive_ifdef_level( 0 )
{}
//____________________________________________________________________________//
bool
config_file_iterator::Impl::get_next_line( cstring& line )
{
bool broken_line = false;
line.clear();
while( !m_curr_level->m_stream.eof() || !!m_curr_level->m_parent.get() ) {
// 10. Switch to upper include level if current one is finished
// 20. Read/append next file line
// 30. Increment line number
// 40. Remove comments
// 50. Remove trailing and leading spaces
// 60. Skip empty string
// 70. Concatenate broken lines if needed. Put the result into line
// 80. If line is not completed, try to finish it by reading the next line
// 90. Process command line
// 100. Substitute macros references with their definitions
// 110. Next line found.
if( m_curr_level->m_stream.eof() ) { // 10 //
m_curr_level = m_curr_level->m_parent;
continue;
}
std::ifstream& input = m_curr_level->m_stream;
char_type* buffer_insert_pos = broken_line ? m_buffer.get() + line.size() : m_buffer.get();
input.getline( buffer_insert_pos, (std::streamsize)(m_buffer_size - line.size()), // 20 //
m_line_delimeter );
cstring next_line( buffer_insert_pos,
input.gcount() > 0
? buffer_insert_pos + (input.eof() ? input.gcount() : (input.gcount()-1))
: buffer_insert_pos );
m_curr_level->m_curr_location.second++; // 30 //
cstring::size_type comment_pos = next_line.find( m_sl_comment_delimeter );
if( comment_pos != cstring::npos )
next_line.trim_right( next_line.begin()+comment_pos ); // 40 //
if( m_trim_trailing_spaces ) // 50 //
next_line.trim_right();
if( m_trim_leading_spaces && !broken_line )
next_line.trim_left();
if( next_line.is_empty() ) { // 60 //
if( m_skip_empty_lines )
continue;
else
next_line.assign( buffer_insert_pos, buffer_insert_pos );
}
line = broken_line ? cstring( line.begin(), next_line.end() ) : next_line; // 70 //
broken_line = match_back( line, m_line_beak );
if( broken_line ) { // 80 //
line.trim_right( 1 );
continue;
}
if( match_front( line, m_command_delimeter ) ) { // 90 //
process_command_line( line );
continue;
}
if( !is_active_line() )
continue;
substitute_macros( line ); // 100 //
return true; // 110 //
}
BOOST_RT_PARAM_VALIDATE_LOGIC( !broken_line, BOOST_RT_PARAM_LITERAL( "broken line is not completed" ) );
BOOST_RT_PARAM_VALIDATE_LOGIC( m_conditional_states.size() == 0,
BOOST_RT_PARAM_LITERAL( "matching endif command is missing" ) );
return false;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -