/* libobby - Network text editing library * Copyright (C) 2005 0x539 dev group * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "command.hpp" namespace { void unescape(std::string& result) { std::string::size_type pos = 0; while((pos = result.find('\\', pos)) != std::string::npos) { // get_text_param ensures that there is no backslash // at the end of the string switch(result[pos + 1]) { case 'n': result.replace(pos, 2, 1, '\n'); break; case '\'': case '\"': case '\\': result.erase(pos, 1); break; default: throw std::logic_error( "obby::command.cpp::unescape:\n" "Encountered invalid escape sequence" ); } ++ pos; } } std::string::size_type get_next_param(const std::string& list, std::string::size_type pos, std::string& result) { std::string::size_type i = pos; while(i < list.length() && isspace(list[i]) ) ++ i; if(i == list.length() ) return std::string::npos; char str_char = '\0'; if(list[i] == '\"' || list[i] == '\'') { str_char = list[i]; ++ i; } pos = i; bool escape_flag = false; for(; i < list.length(); ++ i) { if(escape_flag) { escape_flag = false; } else { if(list[i] == '\\') escape_flag = true; if(str_char == '\0' && isspace(list[i]) ) break; if(str_char != '\0' && list[i] == str_char) break; } } if(escape_flag) { throw std::logic_error( "Escaping backslash at end of line" ); } if(i == list.length() && str_char != '\0') { throw std::logic_error( "String not closed" ); } if(str_char != '\0') { result = list.substr(pos, i - pos); pos = i + 1; } else { result = list.substr(pos, i - pos); pos = i; } unescape(result); return pos; } } obby::command_query::command_query(const std::string& command, const std::string& paramlist): m_command(command), m_paramlist(paramlist) { } obby::command_query::command_query(const net6::packet& pack, unsigned int& index): m_command(pack.get_param(index).as() ), m_paramlist(pack.get_param(index + 1).as() ) { index += 2; } const std::string& obby::command_query::get_command() const { return m_command; } const std::string& obby::command_query::get_paramlist() const { return m_paramlist; } void obby::command_query::append_packet(net6::packet& pack) const { pack << m_command << m_paramlist; } obby::command_result::command_result(): m_type(NO_REPLY) { } obby::command_result::command_result(type type, const std::string& reply): m_type(type), m_reply(reply) { if(type != REPLY && !reply.empty() ) { throw std::logic_error( "obby::command_result::command_result:\n" "Result type is not reply, but reply string " "is nonempty" ); } } // Reply is only packed when type == REPLY obby::command_result::command_result(const net6::packet& pack, unsigned int& index): m_type(static_cast(pack.get_param(index).as()) ), m_reply( (m_type != REPLY) ? ("") : pack.get_param(index + 1).as() ) { ++ index; if(m_type == REPLY) ++ index; } obby::command_result::type obby::command_result::get_type() const { return m_type; } const std::string& obby::command_result::get_reply() const { return m_reply; } void obby::command_result::append_packet(net6::packet& pack) const { pack << static_cast(m_type); if(m_type == REPLY) pack << m_reply; } obby::command_paramlist::command_paramlist(const std::string& list) { std::string param; std::string::size_type pos = 0; while((pos = get_next_param(list, pos, param)) != std::string::npos) m_params.push_back(param); } obby::command_paramlist::size_type obby::command_paramlist::count() const { return m_params.size(); } const std::string& obby::command_paramlist::value(unsigned int index) const { return m_params.at(index); } obby::command_map::command_map() { add_command( "help", _("Shows all available commands"), sigc::mem_fun(*this, &command_map::on_help) ); } void obby::command_map::add_command(const std::string& name, const std::string& desc, const slot_type& func) { if(m_map.get() == NULL) m_map.reset(new map_type); map_type::const_iterator iter = m_map->find(name); if(iter != m_map->end() ) { throw std::logic_error( "obby::command_map::add_command:\n" "Command exists already" ); } command comm = { name, desc, func }; (*m_map)[name] = comm; } obby::command_result obby::command_map:: exec_command(const user& from, const command_query& query) const { if(m_map.get() == NULL) return command_result::NOT_FOUND; map_type::const_iterator iter = m_map->find(query.get_command() ); if(iter == m_map->end() ) return command_result::NOT_FOUND; return iter->second.func(from, query.get_paramlist()); } obby::command_result obby::command_map::on_help(const user& from, const std::string& paramlist) { std::string reply; for(map_type::const_iterator iter = m_map->begin(); iter != m_map->end(); ++ iter) { reply += iter->second.name; reply += ' '; reply += iter->second.desc; reply += '\n'; } return command_result(command_result::REPLY, reply); } obby::command_queue::command_queue(): m_map(new map_type) { result_event("help").connect( sigc::mem_fun(*this, &command_queue::on_help) ); } void obby::command_queue::query(const command_query& query) { m_commands.push(query); } void obby::command_queue::result(const command_result& result) { if(m_commands.empty() ) { throw std::logic_error( "obby::command_queue::reply:\n" "No query in command queue" ); } command_query query = m_commands.front(); m_commands.pop(); if(result.get_type() == command_result::NOT_FOUND) m_signal_query_failed.emit(query); else (*m_map)[query.get_command()].emit(query, result); } void obby::command_queue::clear() { while(!m_commands.empty() ) m_commands.pop(); } obby::command_queue::signal_result_type obby::command_queue:: result_event(const std::string& command) const { return (*m_map)[command]; } obby::command_queue::signal_query_failed_type obby::command_queue:: query_failed_event() const { return m_signal_query_failed; } obby::command_queue::signal_help_type obby::command_queue::help_event() const { return m_signal_help; } void obby::command_queue::on_help(const command_query& query, const command_result& result) { const std::string& reply = result.get_reply(); std::string::size_type pos = 0, prev = 0; while( (pos = reply.find('\n', pos)) != std::string::npos) { std::string line = reply.substr(prev, pos - prev); std::string::size_type sep = line.find(' '); if(sep == std::string::npos) continue; m_signal_help.emit(line.substr(0, sep), line.substr(sep + 1)); prev = ++ pos; } }