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