286 lines
7.8 KiB
C++
286 lines
7.8 KiB
C++
/* Boost.MultiIndex example of composite keys.
|
|
*
|
|
* Copyright 2003-2008 Joaquin M Lopez Munoz.
|
|
* Distributed under 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/multi_index for library home page.
|
|
*/
|
|
|
|
#if !defined(NDEBUG)
|
|
#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING
|
|
#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE
|
|
#endif
|
|
|
|
#include <boost/call_traits.hpp>
|
|
#include <boost/multi_index_container.hpp>
|
|
#include <boost/multi_index/composite_key.hpp>
|
|
#include <boost/multi_index/member.hpp>
|
|
#include <boost/multi_index/ordered_index.hpp>
|
|
#include <boost/next_prior.hpp>
|
|
#include <boost/tokenizer.hpp>
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <map>
|
|
#include <string>
|
|
|
|
using namespace boost::multi_index;
|
|
|
|
/* A file record maintains some info on name and size as well
|
|
* as a pointer to the directory it belongs (null meaning the root
|
|
* directory.)
|
|
*/
|
|
|
|
struct file_entry
|
|
{
|
|
file_entry(
|
|
std::string name_,unsigned size_,bool is_dir_,const file_entry* dir_):
|
|
name(name_),size(size_),is_dir(is_dir_),dir(dir_)
|
|
{}
|
|
|
|
std::string name;
|
|
unsigned size;
|
|
bool is_dir;
|
|
const file_entry* dir;
|
|
|
|
friend std::ostream& operator<<(std::ostream& os,const file_entry& f)
|
|
{
|
|
os<<f.name<<"\t"<<f.size;
|
|
if(f.is_dir)os<<"\t <dir>";
|
|
return os;
|
|
}
|
|
};
|
|
|
|
/* A file system is just a multi_index_container of entries with indices on
|
|
* file and size. These indices are firstly ordered by directory, as commands
|
|
* work on a current directory basis. Composite keys are just fine to model
|
|
* this.
|
|
* NB: The use of derivation here instead of simple typedef is explained in
|
|
* Compiler specifics: type hiding.
|
|
*/
|
|
|
|
struct name_key:composite_key<
|
|
file_entry,
|
|
BOOST_MULTI_INDEX_MEMBER(file_entry,const file_entry*,dir),
|
|
BOOST_MULTI_INDEX_MEMBER(file_entry,std::string,name)
|
|
>{};
|
|
|
|
struct size_key:composite_key<
|
|
file_entry,
|
|
BOOST_MULTI_INDEX_MEMBER(file_entry,const file_entry* const,dir),
|
|
BOOST_MULTI_INDEX_MEMBER(file_entry,unsigned,size)
|
|
>{};
|
|
|
|
/* see Compiler specifics: composite_key in compilers without partial
|
|
* template specialization, for info on composite_key_result_less
|
|
*/
|
|
|
|
typedef multi_index_container<
|
|
file_entry,
|
|
indexed_by<
|
|
/* primary index sorted by name (inside the same directory) */
|
|
ordered_unique<name_key>,
|
|
/* secondary index sorted by size (inside the same directory) */
|
|
ordered_non_unique<size_key>
|
|
>
|
|
> file_system;
|
|
|
|
/* typedef's of the two indices of file_system */
|
|
|
|
typedef nth_index<file_system,0>::type file_system_by_name;
|
|
typedef nth_index<file_system,1>::type file_system_by_size;
|
|
|
|
/* We build a rudimentary file system simulation out of some global
|
|
* info and a map of commands provided to the user.
|
|
*/
|
|
|
|
static file_system fs; /* the one and only file system */
|
|
static file_system_by_name& fs_by_name=fs; /* name index to fs */
|
|
static file_system_by_size& fs_by_size=get<1>(fs); /* size index to fs */
|
|
static const file_entry* current_dir=0; /* root directory */
|
|
|
|
/* command framework */
|
|
|
|
/* A command provides an execute memfun fed with the corresponding params
|
|
* (first param is stripped off as it serves to identify the command
|
|
* currently being used.)
|
|
*/
|
|
|
|
typedef boost::tokenizer<boost::char_separator<char> > command_tokenizer;
|
|
|
|
class command
|
|
{
|
|
public:
|
|
virtual ~command(){}
|
|
virtual void execute(
|
|
command_tokenizer::iterator tok1,command_tokenizer::iterator tok2)=0;
|
|
};
|
|
|
|
/* available commands */
|
|
|
|
/* cd: syntax cd [.|..|<directory>] */
|
|
|
|
class command_cd:public command
|
|
{
|
|
public:
|
|
virtual void execute(
|
|
command_tokenizer::iterator tok1,command_tokenizer::iterator tok2)
|
|
{
|
|
if(tok1==tok2)return;
|
|
std::string dir=*tok1++;
|
|
|
|
if(dir==".")return;
|
|
if(dir==".."){
|
|
if(current_dir)current_dir=current_dir->dir;
|
|
return;
|
|
}
|
|
|
|
file_system_by_name::iterator it=fs.find(
|
|
boost::make_tuple(current_dir,dir));
|
|
if(it==fs.end()){
|
|
std::cout<<"non-existent directory"<<std::endl;
|
|
return;
|
|
}
|
|
if(!it->is_dir){
|
|
std::cout<<dir<<" is not a directory"<<std::endl;
|
|
return;
|
|
}
|
|
current_dir=&*it;
|
|
}
|
|
};
|
|
static command_cd cd;
|
|
|
|
/* ls: syntax ls [-s] */
|
|
|
|
class command_ls:public command
|
|
{
|
|
public:
|
|
virtual void execute(
|
|
command_tokenizer::iterator tok1,command_tokenizer::iterator tok2)
|
|
{
|
|
std::string option;
|
|
if(tok1!=tok2)option=*tok1++;
|
|
|
|
if(!option.empty()){
|
|
if(option!="-s"){
|
|
std::cout<<"incorrect parameter"<<std::endl;
|
|
return;
|
|
}
|
|
|
|
/* list by size */
|
|
|
|
file_system_by_size::iterator it0,it1;
|
|
boost::tie(it0,it1)=fs_by_size.equal_range(
|
|
boost::make_tuple(current_dir));
|
|
std::copy(it0,it1,std::ostream_iterator<file_entry>(std::cout,"\n"));
|
|
|
|
return;
|
|
}
|
|
|
|
/* list by name */
|
|
|
|
file_system_by_name::iterator it0,it1;
|
|
boost::tie(it0,it1)=fs.equal_range(boost::make_tuple(current_dir));
|
|
std::copy(it0,it1,std::ostream_iterator<file_entry>(std::cout,"\n"));
|
|
}
|
|
};
|
|
static command_ls ls;
|
|
|
|
/* mkdir: syntax mkdir <directory> */
|
|
|
|
class command_mkdir:public command
|
|
{
|
|
public:
|
|
virtual void execute(
|
|
command_tokenizer::iterator tok1,command_tokenizer::iterator tok2)
|
|
{
|
|
std::string dir;
|
|
if(tok1!=tok2)dir=*tok1++;
|
|
|
|
if(dir.empty()){
|
|
std::cout<<"missing parameter"<<std::endl;
|
|
return;
|
|
}
|
|
|
|
if(dir=="."||dir==".."){
|
|
std::cout<<"incorrect parameter"<<std::endl;
|
|
return;
|
|
}
|
|
|
|
if(!fs.insert(file_entry(dir,0,true,current_dir)).second){
|
|
std::cout<<"directory already exists"<<std::endl;
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
static command_mkdir mkdir;
|
|
|
|
/* table of commands, a map from command names to class command pointers */
|
|
|
|
typedef std::map<std::string,command*> command_table;
|
|
static command_table cmt;
|
|
|
|
int main()
|
|
{
|
|
/* fill the file system with some data */
|
|
|
|
file_system::iterator it0,it1;
|
|
|
|
fs.insert(file_entry("usr.cfg",240,false,0));
|
|
fs.insert(file_entry("memo.txt",2430,false,0));
|
|
it0=fs.insert(file_entry("dev",0,true,0)).first;
|
|
fs.insert(file_entry("tty0",128,false,&*it0));
|
|
fs.insert(file_entry("tty1",128,false,&*it0));
|
|
it0=fs.insert(file_entry("usr",0,true,0)).first;
|
|
it1=fs.insert(file_entry("bin",0,true,&*it0)).first;
|
|
fs.insert(file_entry("bjam",172032,false,&*it1));
|
|
it0=fs.insert(file_entry("home",0,true,0)).first;
|
|
it1=fs.insert(file_entry("andy",0,true,&*it0)).first;
|
|
fs.insert(file_entry("logo.jpg",5345,false,&*it1)).first;
|
|
fs.insert(file_entry("foo.cpp",890,false,&*it1)).first;
|
|
fs.insert(file_entry("foo.hpp",93,false,&*it1)).first;
|
|
fs.insert(file_entry("foo.html",750,false,&*it1)).first;
|
|
fs.insert(file_entry("a.obj",12302,false,&*it1)).first;
|
|
fs.insert(file_entry(".bash_history",8780,false,&*it1)).first;
|
|
it1=fs.insert(file_entry("rachel",0,true,&*it0)).first;
|
|
fs.insert(file_entry("test.py",650,false,&*it1)).first;
|
|
fs.insert(file_entry("todo.txt",241,false,&*it1)).first;
|
|
fs.insert(file_entry(".bash_history",9510,false,&*it1)).first;
|
|
|
|
/* fill the command table */
|
|
|
|
cmt["cd"] =&cd;
|
|
cmt["ls"] =&ls;
|
|
cmt["mkdir"]=&mkdir;
|
|
|
|
/* main looop */
|
|
|
|
for(;;){
|
|
/* print out the current directory and the prompt symbol */
|
|
|
|
if(current_dir)std::cout<<current_dir->name;
|
|
std::cout<<">";
|
|
|
|
/* get an input line from the user: if empty, exit the program */
|
|
|
|
std::string com;
|
|
std::getline(std::cin,com);
|
|
command_tokenizer tok(com,boost::char_separator<char>(" \t\n"));
|
|
if(tok.begin()==tok.end())break; /* null command, exit */
|
|
|
|
/* select the corresponding command and execute it */
|
|
|
|
command_table::iterator it=cmt.find(*tok.begin());
|
|
if(it==cmt.end()){
|
|
std::cout<<"invalid command"<<std::endl;
|
|
continue;
|
|
}
|
|
|
|
it->second->execute(boost::next(tok.begin()),tok.end());
|
|
}
|
|
|
|
return 0;
|
|
}
|