• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Boost.MultiIndex example of composite keys.
2  *
3  * Copyright 2003-2008 Joaquin M Lopez Munoz.
4  * Distributed under the Boost Software License, Version 1.0.
5  * (See accompanying file LICENSE_1_0.txt or copy at
6  * http://www.boost.org/LICENSE_1_0.txt)
7  *
8  * See http://www.boost.org/libs/multi_index for library home page.
9  */
10 
11 #if !defined(NDEBUG)
12 #define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING
13 #define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE
14 #endif
15 
16 #include <boost/call_traits.hpp>
17 #include <boost/multi_index_container.hpp>
18 #include <boost/multi_index/composite_key.hpp>
19 #include <boost/multi_index/member.hpp>
20 #include <boost/multi_index/ordered_index.hpp>
21 #include <boost/next_prior.hpp>
22 #include <boost/tokenizer.hpp>
23 #include <functional>
24 #include <iostream>
25 #include <iterator>
26 #include <map>
27 #include <string>
28 
29 using namespace boost::multi_index;
30 
31 /* A file record maintains some info on name and size as well
32  * as a pointer to the directory it belongs (null meaning the root
33  * directory.)
34  */
35 
36 struct file_entry
37 {
file_entryfile_entry38   file_entry(
39     std::string name_,unsigned size_,bool is_dir_,const file_entry* dir_):
40     name(name_),size(size_),is_dir(is_dir_),dir(dir_)
41   {}
42 
43   std::string       name;
44   unsigned          size;
45   bool              is_dir;
46   const file_entry* dir;
47 
operator <<(std::ostream & os,const file_entry & f)48   friend std::ostream& operator<<(std::ostream& os,const file_entry& f)
49   {
50     os<<f.name<<"\t"<<f.size;
51     if(f.is_dir)os<<"\t <dir>";
52     return os;
53   }
54 };
55 
56 /* A file system is just a multi_index_container of entries with indices on
57  * file and size. These indices are firstly ordered by directory, as commands
58  * work on a current directory basis. Composite keys are just fine to model
59  * this.
60  * NB: The use of derivation here instead of simple typedef is explained in
61  * Compiler specifics: type hiding.
62  */
63 
64 struct name_key:composite_key<
65   file_entry,
66   BOOST_MULTI_INDEX_MEMBER(file_entry,const file_entry*,dir),
67   BOOST_MULTI_INDEX_MEMBER(file_entry,std::string,name)
68 >{};
69 
70 struct size_key:composite_key<
71   file_entry,
72   BOOST_MULTI_INDEX_MEMBER(file_entry,const file_entry* const,dir),
73   BOOST_MULTI_INDEX_MEMBER(file_entry,unsigned,size)
74 >{};
75 
76 /* see Compiler specifics: composite_key in compilers without partial
77  * template specialization, for info on composite_key_result_less
78  */
79 
80 typedef multi_index_container<
81   file_entry,
82   indexed_by<
83     /* primary index sorted by name (inside the same directory) */
84     ordered_unique<name_key>,
85     /* secondary index sorted by size (inside the same directory) */
86     ordered_non_unique<size_key>
87   >
88 > file_system;
89 
90 /* typedef's of the two indices of file_system */
91 
92 typedef nth_index<file_system,0>::type file_system_by_name;
93 typedef nth_index<file_system,1>::type file_system_by_size;
94 
95 /* We build a rudimentary file system simulation out of some global
96  * info and a map of commands provided to the user.
97  */
98 
99 static file_system fs;                 /* the one and only file system */
100 static file_system_by_name& fs_by_name=fs;         /* name index to fs */
101 static file_system_by_size& fs_by_size=get<1>(fs); /* size index to fs */
102 static const file_entry* current_dir=0;            /* root directory   */
103 
104 /* command framework */
105 
106 /* A command provides an execute memfun fed with the corresponding params
107  * (first param is stripped off as it serves to identify the command
108  * currently being used.)
109  */
110 
111 typedef boost::tokenizer<boost::char_separator<char> > command_tokenizer;
112 
113 class command
114 {
115 public:
~command()116   virtual ~command(){}
117   virtual void execute(
118     command_tokenizer::iterator tok1,command_tokenizer::iterator tok2)=0;
119 };
120 
121 /* available commands */
122 
123 /* cd: syntax cd [.|..|<directory>] */
124 
125 class command_cd:public command
126 {
127 public:
execute(command_tokenizer::iterator tok1,command_tokenizer::iterator tok2)128   virtual void execute(
129     command_tokenizer::iterator tok1,command_tokenizer::iterator tok2)
130   {
131     if(tok1==tok2)return;
132     std::string dir=*tok1++;
133 
134     if(dir==".")return;
135     if(dir==".."){
136       if(current_dir)current_dir=current_dir->dir;
137       return;
138     }
139 
140     file_system_by_name::iterator it=fs.find(
141       boost::make_tuple(current_dir,dir));
142     if(it==fs.end()){
143       std::cout<<"non-existent directory"<<std::endl;
144       return;
145     }
146     if(!it->is_dir){
147       std::cout<<dir<<" is not a directory"<<std::endl;
148       return;
149     }
150     current_dir=&*it;
151   }
152 };
153 static command_cd cd;
154 
155 /* ls: syntax ls [-s] */
156 
157 class command_ls:public command
158 {
159 public:
execute(command_tokenizer::iterator tok1,command_tokenizer::iterator tok2)160   virtual void execute(
161     command_tokenizer::iterator tok1,command_tokenizer::iterator tok2)
162   {
163     std::string option;
164     if(tok1!=tok2)option=*tok1++;
165 
166     if(!option.empty()){
167       if(option!="-s"){
168         std::cout<<"incorrect parameter"<<std::endl;
169         return;
170       }
171 
172       /* list by size */
173 
174       file_system_by_size::iterator it0,it1;
175       boost::tie(it0,it1)=fs_by_size.equal_range(
176         boost::make_tuple(current_dir));
177       std::copy(it0,it1,std::ostream_iterator<file_entry>(std::cout,"\n"));
178 
179       return;
180     }
181 
182     /* list by name */
183 
184     file_system_by_name::iterator it0,it1;
185     boost::tie(it0,it1)=fs.equal_range(boost::make_tuple(current_dir));
186     std::copy(it0,it1,std::ostream_iterator<file_entry>(std::cout,"\n"));
187   }
188 };
189 static command_ls ls;
190 
191 /* mkdir: syntax mkdir <directory> */
192 
193 class command_mkdir:public command
194 {
195 public:
execute(command_tokenizer::iterator tok1,command_tokenizer::iterator tok2)196   virtual void execute(
197     command_tokenizer::iterator tok1,command_tokenizer::iterator tok2)
198   {
199     std::string dir;
200     if(tok1!=tok2)dir=*tok1++;
201 
202     if(dir.empty()){
203       std::cout<<"missing parameter"<<std::endl;
204       return;
205     }
206 
207     if(dir=="."||dir==".."){
208       std::cout<<"incorrect parameter"<<std::endl;
209       return;
210     }
211 
212     if(!fs.insert(file_entry(dir,0,true,current_dir)).second){
213       std::cout<<"directory already exists"<<std::endl;
214       return;
215     }
216   }
217 };
218 static command_mkdir mkdir;
219 
220 /* table of commands, a map from command names to class command pointers */
221 
222 typedef std::map<std::string,command*> command_table;
223 static command_table cmt;
224 
main()225 int main()
226 {
227   /* fill the file system with some data */
228 
229   file_system::iterator it0,it1;
230 
231   fs.insert(file_entry("usr.cfg",240,false,0));
232   fs.insert(file_entry("memo.txt",2430,false,0));
233   it0=fs.insert(file_entry("dev",0,true,0)).first;
234     fs.insert(file_entry("tty0",128,false,&*it0));
235     fs.insert(file_entry("tty1",128,false,&*it0));
236   it0=fs.insert(file_entry("usr",0,true,0)).first;
237     it1=fs.insert(file_entry("bin",0,true,&*it0)).first;
238       fs.insert(file_entry("bjam",172032,false,&*it1));
239   it0=fs.insert(file_entry("home",0,true,0)).first;
240     it1=fs.insert(file_entry("andy",0,true,&*it0)).first;
241       fs.insert(file_entry("logo.jpg",5345,false,&*it1)).first;
242       fs.insert(file_entry("foo.cpp",890,false,&*it1)).first;
243       fs.insert(file_entry("foo.hpp",93,false,&*it1)).first;
244       fs.insert(file_entry("foo.html",750,false,&*it1)).first;
245       fs.insert(file_entry("a.obj",12302,false,&*it1)).first;
246       fs.insert(file_entry(".bash_history",8780,false,&*it1)).first;
247     it1=fs.insert(file_entry("rachel",0,true,&*it0)).first;
248       fs.insert(file_entry("test.py",650,false,&*it1)).first;
249       fs.insert(file_entry("todo.txt",241,false,&*it1)).first;
250       fs.insert(file_entry(".bash_history",9510,false,&*it1)).first;
251 
252   /* fill the command table */
253 
254   cmt["cd"]   =&cd;
255   cmt["ls"]   =&ls;
256   cmt["mkdir"]=&mkdir;
257 
258   /* main looop */
259 
260   for(;;){
261     /* print out the current directory and the prompt symbol */
262 
263     if(current_dir)std::cout<<current_dir->name;
264     std::cout<<">";
265 
266     /* get an input line from the user: if empty, exit the program */
267 
268     std::string com;
269     std::getline(std::cin,com);
270     command_tokenizer tok(com,boost::char_separator<char>(" \t\n"));
271     if(tok.begin()==tok.end())break; /* null command, exit */
272 
273     /* select the corresponding command and execute it */
274 
275     command_table::iterator it=cmt.find(*tok.begin());
276     if(it==cmt.end()){
277       std::cout<<"invalid command"<<std::endl;
278       continue;
279     }
280 
281     it->second->execute(boost::next(tok.begin()),tok.end());
282   }
283 
284   return 0;
285 }
286