1 /* libobby - Network text editing library
2 * Copyright (C) 2005 0x539 dev group
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this program; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include "command.hpp"
20
21 namespace
22 {
unescape(std::string & result)23 void unescape(std::string& result)
24 {
25 std::string::size_type pos = 0;
26 while((pos = result.find('\\', pos)) != std::string::npos)
27 {
28 // get_text_param ensures that there is no backslash
29 // at the end of the string
30 switch(result[pos + 1])
31 {
32 case 'n':
33 result.replace(pos, 2, 1, '\n');
34 break;
35 case '\'':
36 case '\"':
37 case '\\':
38 result.erase(pos, 1);
39 break;
40 default:
41 throw std::logic_error(
42 "obby::command.cpp::unescape:\n"
43 "Encountered invalid escape sequence"
44 );
45 }
46
47 ++ pos;
48 }
49 }
50
get_next_param(const std::string & list,std::string::size_type pos,std::string & result)51 std::string::size_type get_next_param(const std::string& list,
52 std::string::size_type pos,
53 std::string& result)
54 {
55 std::string::size_type i = pos;
56
57 while(i < list.length() && isspace(list[i]) )
58 ++ i;
59
60 if(i == list.length() )
61 return std::string::npos;
62
63 char str_char = '\0';
64 if(list[i] == '\"' || list[i] == '\'')
65 {
66 str_char = list[i];
67 ++ i;
68 }
69
70 pos = i;
71 bool escape_flag = false;
72
73 for(; i < list.length(); ++ i)
74 {
75 if(escape_flag)
76 {
77 escape_flag = false;
78 }
79 else
80 {
81 if(list[i] == '\\')
82 escape_flag = true;
83
84 if(str_char == '\0' && isspace(list[i]) )
85 break;
86
87 if(str_char != '\0' && list[i] == str_char)
88 break;
89 }
90 }
91
92 if(escape_flag)
93 {
94 throw std::logic_error(
95 "Escaping backslash at end of line"
96 );
97 }
98
99 if(i == list.length() && str_char != '\0')
100 {
101 throw std::logic_error(
102 "String not closed"
103 );
104 }
105
106 if(str_char != '\0')
107 {
108 result = list.substr(pos, i - pos);
109 pos = i + 1;
110 }
111 else
112 {
113 result = list.substr(pos, i - pos);
114 pos = i;
115 }
116
117 unescape(result);
118 return pos;
119 }
120 }
121
command_query(const std::string & command,const std::string & paramlist)122 obby::command_query::command_query(const std::string& command,
123 const std::string& paramlist):
124 m_command(command), m_paramlist(paramlist)
125 {
126 }
127
command_query(const net6::packet & pack,unsigned int & index)128 obby::command_query::command_query(const net6::packet& pack,
129 unsigned int& index):
130 m_command(pack.get_param(index).as<std::string>() ),
131 m_paramlist(pack.get_param(index + 1).as<std::string>() )
132 {
133 index += 2;
134 }
135
get_command() const136 const std::string& obby::command_query::get_command() const
137 {
138 return m_command;
139 }
140
get_paramlist() const141 const std::string& obby::command_query::get_paramlist() const
142 {
143 return m_paramlist;
144 }
145
append_packet(net6::packet & pack) const146 void obby::command_query::append_packet(net6::packet& pack) const
147 {
148 pack << m_command << m_paramlist;
149 }
150
command_result()151 obby::command_result::command_result():
152 m_type(NO_REPLY)
153 {
154 }
155
command_result(type type,const std::string & reply)156 obby::command_result::command_result(type type, const std::string& reply):
157 m_type(type), m_reply(reply)
158 {
159 if(type != REPLY && !reply.empty() )
160 {
161 throw std::logic_error(
162 "obby::command_result::command_result:\n"
163 "Result type is not reply, but reply string "
164 "is nonempty"
165 );
166 }
167 }
168
169 // Reply is only packed when type == REPLY
command_result(const net6::packet & pack,unsigned int & index)170 obby::command_result::command_result(const net6::packet& pack,
171 unsigned int& index):
172 m_type(static_cast<type>(pack.get_param(index).as<unsigned int>()) ),
173 m_reply(
174 (m_type != REPLY) ?
175 ("") :
176 pack.get_param(index + 1).as<std::string>()
177 )
178 {
179 ++ index;
180 if(m_type == REPLY) ++ index;
181 }
182
get_type() const183 obby::command_result::type obby::command_result::get_type() const
184 {
185 return m_type;
186 }
187
get_reply() const188 const std::string& obby::command_result::get_reply() const
189 {
190 return m_reply;
191 }
192
append_packet(net6::packet & pack) const193 void obby::command_result::append_packet(net6::packet& pack) const
194 {
195 pack << static_cast<unsigned int>(m_type);
196 if(m_type == REPLY) pack << m_reply;
197 }
198
command_paramlist(const std::string & list)199 obby::command_paramlist::command_paramlist(const std::string& list)
200 {
201 std::string param;
202 std::string::size_type pos = 0;
203 while((pos = get_next_param(list, pos, param)) != std::string::npos)
204 m_params.push_back(param);
205 }
206
count() const207 obby::command_paramlist::size_type obby::command_paramlist::count() const
208 {
209 return m_params.size();
210 }
211
value(unsigned int index) const212 const std::string& obby::command_paramlist::value(unsigned int index) const
213 {
214 return m_params.at(index);
215 }
216
command_map()217 obby::command_map::command_map()
218 {
219 add_command(
220 "help",
221 _("Shows all available commands"),
222 sigc::mem_fun(*this, &command_map::on_help)
223 );
224 }
225
add_command(const std::string & name,const std::string & desc,const slot_type & func)226 void obby::command_map::add_command(const std::string& name,
227 const std::string& desc,
228 const slot_type& func)
229 {
230 if(m_map.get() == NULL) m_map.reset(new map_type);
231
232 map_type::const_iterator iter = m_map->find(name);
233 if(iter != m_map->end() )
234 {
235 throw std::logic_error(
236 "obby::command_map::add_command:\n"
237 "Command exists already"
238 );
239 }
240
241 command comm = { name, desc, func };
242 (*m_map)[name] = comm;
243 }
244
245 obby::command_result obby::command_map::
exec_command(const user & from,const command_query & query) const246 exec_command(const user& from,
247 const command_query& query) const
248 {
249 if(m_map.get() == NULL) return command_result::NOT_FOUND;
250 map_type::const_iterator iter = m_map->find(query.get_command() );
251 if(iter == m_map->end() ) return command_result::NOT_FOUND;
252 return iter->second.func(from, query.get_paramlist());
253 }
254
on_help(const user & from,const std::string & paramlist)255 obby::command_result obby::command_map::on_help(const user& from,
256 const std::string& paramlist)
257 {
258 std::string reply;
259
260 for(map_type::const_iterator iter = m_map->begin();
261 iter != m_map->end();
262 ++ iter)
263 {
264 reply += iter->second.name;
265 reply += ' ';
266 reply += iter->second.desc;
267 reply += '\n';
268 }
269
270 return command_result(command_result::REPLY, reply);
271 }
272
command_queue()273 obby::command_queue::command_queue():
274 m_map(new map_type)
275 {
276 result_event("help").connect(
277 sigc::mem_fun(*this, &command_queue::on_help)
278 );
279 }
280
query(const command_query & query)281 void obby::command_queue::query(const command_query& query)
282 {
283 m_commands.push(query);
284 }
285
result(const command_result & result)286 void obby::command_queue::result(const command_result& result)
287 {
288 if(m_commands.empty() )
289 {
290 throw std::logic_error(
291 "obby::command_queue::reply:\n"
292 "No query in command queue"
293 );
294 }
295
296 command_query query = m_commands.front();
297 m_commands.pop();
298
299 if(result.get_type() == command_result::NOT_FOUND)
300 m_signal_query_failed.emit(query);
301 else
302 (*m_map)[query.get_command()].emit(query, result);
303 }
304
clear()305 void obby::command_queue::clear()
306 {
307 while(!m_commands.empty() )
308 m_commands.pop();
309 }
310
311 obby::command_queue::signal_result_type obby::command_queue::
result_event(const std::string & command) const312 result_event(const std::string& command) const
313 {
314 return (*m_map)[command];
315 }
316
317 obby::command_queue::signal_query_failed_type obby::command_queue::
query_failed_event() const318 query_failed_event() const
319 {
320 return m_signal_query_failed;
321 }
322
help_event() const323 obby::command_queue::signal_help_type obby::command_queue::help_event() const
324 {
325 return m_signal_help;
326 }
327
on_help(const command_query & query,const command_result & result)328 void obby::command_queue::on_help(const command_query& query,
329 const command_result& result)
330 {
331 const std::string& reply = result.get_reply();
332 std::string::size_type pos = 0, prev = 0;
333
334 while( (pos = reply.find('\n', pos)) != std::string::npos)
335 {
336 std::string line = reply.substr(prev, pos - prev);
337 std::string::size_type sep = line.find(' ');
338 if(sep == std::string::npos) continue;
339
340 m_signal_help.emit(line.substr(0, sep), line.substr(sep + 1));
341 prev = ++ pos;
342 }
343 }
344