Migrate the rest to function_ptr_command
[lsnes.git] / command.cpp
blob18b7020fc82e7676f13397da9e8d495f75970026
1 #include "command.hpp"
2 #include "misc.hpp"
3 #include "zip.hpp"
4 #include <set>
5 #include <map>
7 namespace
9 std::map<std::string, command*>* commands;
10 std::set<std::string> command_stack;
11 std::map<std::string, std::list<std::string>> aliases;
13 function_ptr_command run_script("run-script", "run file as a script",
14 "Syntax: run-script <file>\nRuns file <file> just as it would have been entered in the command line\n",
15 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
16 if(args == "")
17 throw std::runtime_error("Filename needed");
18 std::istream* o = NULL;
19 try {
20 o = &open_file_relative(args, "");
21 messages << "Running '" << args << "'" << std::endl;
22 std::string line;
23 while(std::getline(*o, line))
24 command::invokeC(line);
25 delete o;
26 } catch(std::exception& e) {
27 if(o)
28 delete o;
29 throw;
31 });
33 function_ptr_command show_aliases("show-aliases", "show aliases",
34 "Syntax: show-aliases\nShow expansions of all aliases\n",
35 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
36 if(args != "")
37 throw std::runtime_error("This command does not take parameters");
38 for(auto i = aliases.begin(); i != aliases.end(); i++)
39 for(auto j = i->second.begin(); j != i->second.end(); j++)
40 messages << "alias " << i->first << " " << *j << std::endl;
41 });
43 function_ptr_command unalias_command("unalias-command", "unalias a command",
44 "Syntax: unalias-command <aliasname>\nClear expansion of alias <aliasname>\n",
45 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
46 tokensplitter t(args);
47 std::string aliasname = t;
48 if(t)
49 throw std::runtime_error("This command only takes one argument");
50 if(aliasname.length() == 0 || aliasname[0] == '?' || aliasname[0] == '*')
51 throw std::runtime_error("Illegal alias name");
52 aliases[aliasname].clear();
53 messages << "Command '" << aliasname << "' unaliased" << std::endl;
54 });
56 function_ptr_command alias_command("alias-command", "alias a command",
57 "Syntax: alias-command <aliasname> <command>\nAppend <command> to expansion of alias <aliasname>\n"
58 "Valid alias names can't be empty nor start with '*' or '?'\n",
59 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
60 tokensplitter t(args);
61 std::string aliasname = t;
62 std::string command = t.tail();
63 if(command == "")
64 throw std::runtime_error("Alias name and command needed");
65 if(aliasname.length() == 0 || aliasname[0] == '?' || aliasname[0] == '*')
66 throw std::runtime_error("Illegal alias name");
67 aliases[aliasname].push_back(command);
68 messages << "Command '" << aliasname << "' aliased to '" << command << "'" << std::endl;
69 });
72 command::command(const std::string& cmd) throw(std::bad_alloc)
74 if(!commands)
75 commands = new std::map<std::string, command*>();
76 if(commands->count(cmd))
77 std::cerr << "WARNING: Command collision for " << cmd << "!" << std::endl;
78 (*commands)[commandname = cmd] = this;
81 command::~command() throw()
83 if(!commands)
84 return;
85 commands->erase(commandname);
88 void command::invokeC(const std::string& cmd) throw()
90 try {
91 if(command_stack.count(cmd)) {
92 messages << "Can not invoke recursively: " << cmd << std::endl;
93 return;
95 command_stack.insert(cmd);
96 std::string cmd2 = cmd;
97 if(cmd2 == "?") {
98 //The special ? command.
99 if(commands) {
100 for(auto i = commands->begin(); i != commands->end(); ++i)
101 messages << i->first << ": " << i->second->get_short_help() << std::endl;
103 command_stack.erase(cmd);
104 return;
106 if(cmd2.length() > 1 && cmd2[0] == '?') {
107 //?command.
108 size_t split = cmd2.find_first_of(" \t");
109 std::string rcmd;
110 if(split >= cmd2.length())
111 rcmd = cmd2.substr(1);
112 else
113 rcmd = cmd2.substr(1, split - 1);
114 if(rcmd.length() > 0 && rcmd[0] != '*') {
115 //This may be an alias.
116 std::string aname = cmd2.substr(1);
117 if(aliases.count(aname)) {
118 //Yup.
119 messages << aname << " is an alias for: " << std::endl;
120 size_t j = 0;
121 for(auto i = aliases[aname].begin(); i != aliases[aname].end(); ++i, ++j)
122 messages << "#" + (j + 1) << ": " << *i << std::endl;
123 command_stack.erase(cmd);
124 return;
127 if(rcmd.length() > 0 && rcmd[0] == '*')
128 rcmd = rcmd.substr(1);
129 if(!commands || !commands->count(rcmd)) {
130 if(rcmd != "")
131 messages << "Unknown command '" << rcmd << "'" << std::endl;
132 command_stack.erase(cmd);
133 return;
135 messages << (*commands)[rcmd]->get_long_help() << std::endl;
136 command_stack.erase(cmd);
137 return;
139 bool may_be_alias_expanded = true;
140 if(cmd2.length() > 0 && cmd2[0] == '*') {
141 may_be_alias_expanded = false;
142 cmd2 = cmd2.substr(1);
144 if(may_be_alias_expanded && aliases.count(cmd2)) {
145 for(auto i = aliases[cmd2].begin(); i != aliases[cmd2].end(); ++i)
146 invokeC(*i);
147 command_stack.erase(cmd);
148 return;
150 try {
151 size_t split = cmd2.find_first_of(" \t");
152 std::string rcmd;
153 if(split >= cmd2.length())
154 rcmd = cmd2;
155 else
156 rcmd = cmd2.substr(0, split);
157 split = cmd2.find_first_not_of(" \t", split);
158 std::string args;
159 if(split < cmd2.length())
160 args = cmd2.substr(split);
161 command* cmdh = NULL;
162 if(commands && commands->count(rcmd))
163 cmdh = (*commands)[rcmd];
164 if(!cmdh) {
165 messages << "Unknown command '" << rcmd << "'" << std::endl;
166 command_stack.erase(cmd);
167 return;
169 cmdh->invoke(args);
170 command_stack.erase(cmd);
171 return;
172 } catch(std::bad_alloc& e) {
173 OOM_panic();
174 } catch(std::exception& e) {
175 messages << "Error: " << e.what() << std::endl;
176 command_stack.erase(cmd);
177 return;
179 } catch(std::bad_alloc& e) {
180 OOM_panic();
184 std::string command::get_short_help() throw(std::bad_alloc)
186 return "No description available";
189 std::string command::get_long_help() throw(std::bad_alloc)
191 return "No help available on command " + commandname;
194 tokensplitter::tokensplitter(const std::string& _line) throw(std::bad_alloc)
196 line = _line;
197 position = 0;
200 tokensplitter::operator bool() throw()
202 return (position < line.length());
205 tokensplitter::operator std::string() throw(std::bad_alloc)
207 size_t nextp, oldp = position;
208 nextp = line.find_first_of(" \t", position);
209 if(nextp > line.length()) {
210 position = line.length();
211 return line.substr(oldp);
212 } else {
213 position = nextp;
214 while(position < line.length() && (line[position] == ' ' || line[position] == '\t'))
215 position++;
216 return line.substr(oldp, nextp - oldp);
220 std::string tokensplitter::tail() throw(std::bad_alloc)
222 return line.substr(position);
225 function_ptr_command::function_ptr_command(const std::string& name, const std::string& _description,
226 const std::string& _help, void (*_fn)(const std::string& arguments)) throw(std::bad_alloc)
227 : command(name)
229 description = _description;
230 help = _help;
231 fn = _fn;
234 void function_ptr_command::invoke(const std::string& args) throw(std::bad_alloc, std::runtime_error)
236 fn(args);
239 std::string function_ptr_command::get_short_help() throw(std::bad_alloc)
241 return description;
244 std::string function_ptr_command::get_long_help() throw(std::bad_alloc)
246 return help;
249 function_ptr_command::~function_ptr_command() throw()