Make joysticks actually work
[lsnes.git] / command.cpp
blobcd5207ac2b61e0f5474343c33cd98a8875206ab8
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<arg_filename> 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 [](arg_filename filename) throw(std::bad_alloc, std::runtime_error) {
16 std::istream* o = NULL;
17 try {
18 o = &open_file_relative(filename, "");
19 messages << "Running '" << std::string(filename) << "'" << std::endl;
20 std::string line;
21 while(std::getline(*o, line))
22 command::invokeC(line);
23 delete o;
24 } catch(std::exception& e) {
25 if(o)
26 delete o;
27 throw;
29 });
31 function_ptr_command<> show_aliases("show-aliases", "show aliases",
32 "Syntax: show-aliases\nShow expansions of all aliases\n",
33 []() throw(std::bad_alloc, std::runtime_error) {
34 for(auto i = aliases.begin(); i != aliases.end(); i++)
35 for(auto j = i->second.begin(); j != i->second.end(); j++)
36 messages << "alias " << i->first << " " << *j << std::endl;
37 });
39 function_ptr_command<tokensplitter&> unalias_command("unalias-command", "unalias a command",
40 "Syntax: unalias-command <aliasname>\nClear expansion of alias <aliasname>\n",
41 [](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
42 std::string aliasname = t;
43 if(t)
44 throw std::runtime_error("This command only takes one argument");
45 if(aliasname.length() == 0 || aliasname[0] == '?' || aliasname[0] == '*')
46 throw std::runtime_error("Illegal alias name");
47 aliases[aliasname].clear();
48 messages << "Command '" << aliasname << "' unaliased" << std::endl;
49 });
51 function_ptr_command<tokensplitter&> alias_command("alias-command", "alias a command",
52 "Syntax: alias-command <aliasname> <command>\nAppend <command> to expansion of alias <aliasname>\n"
53 "Valid alias names can't be empty nor start with '*' or '?'\n",
54 [](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
55 std::string aliasname = t;
56 std::string command = t.tail();
57 if(command == "")
58 throw std::runtime_error("Alias name and command needed");
59 if(aliasname.length() == 0 || aliasname[0] == '?' || aliasname[0] == '*')
60 throw std::runtime_error("Illegal alias name");
61 aliases[aliasname].push_back(command);
62 messages << "Command '" << aliasname << "' aliased to '" << command << "'" << std::endl;
63 });
66 command::command(const std::string& cmd) throw(std::bad_alloc)
68 if(!commands)
69 commands = new std::map<std::string, command*>();
70 if(commands->count(cmd))
71 std::cerr << "WARNING: Command collision for " << cmd << "!" << std::endl;
72 (*commands)[commandname = cmd] = this;
75 command::~command() throw()
77 if(!commands)
78 return;
79 commands->erase(commandname);
82 void command::invokeC(const std::string& cmd) throw()
84 try {
85 std::string cmd2 = cmd;
86 if(cmd2 == "?") {
87 //The special ? command.
88 if(commands) {
89 for(auto i = commands->begin(); i != commands->end(); ++i)
90 messages << i->first << ": " << i->second->get_short_help() << std::endl;
92 return;
94 if(cmd2.length() > 1 && cmd2[0] == '?') {
95 //?command.
96 size_t split = cmd2.find_first_of(" \t");
97 std::string rcmd;
98 if(split >= cmd2.length())
99 rcmd = cmd2.substr(1);
100 else
101 rcmd = cmd2.substr(1, split - 1);
102 if(rcmd.length() > 0 && rcmd[0] != '*') {
103 //This may be an alias.
104 std::string aname = cmd2.substr(1);
105 if(aliases.count(aname)) {
106 //Yup.
107 messages << aname << " is an alias for: " << std::endl;
108 size_t j = 0;
109 for(auto i = aliases[aname].begin(); i != aliases[aname].end(); ++i, ++j)
110 messages << "#" + (j + 1) << ": " << *i << std::endl;
111 return;
114 if(rcmd.length() > 0 && rcmd[0] == '*')
115 rcmd = rcmd.substr(1);
116 if(!commands || !commands->count(rcmd)) {
117 if(rcmd != "")
118 messages << "Unknown command '" << rcmd << "'" << std::endl;
119 return;
121 messages << (*commands)[rcmd]->get_long_help() << std::endl;
122 return;
124 bool may_be_alias_expanded = true;
125 if(cmd2.length() > 0 && cmd2[0] == '*') {
126 may_be_alias_expanded = false;
127 cmd2 = cmd2.substr(1);
129 if(may_be_alias_expanded && aliases.count(cmd2)) {
130 for(auto i = aliases[cmd2].begin(); i != aliases[cmd2].end(); ++i)
131 invokeC(*i);
132 return;
134 try {
135 size_t split = cmd2.find_first_of(" \t");
136 std::string rcmd;
137 if(split >= cmd2.length())
138 rcmd = cmd2;
139 else
140 rcmd = cmd2.substr(0, split);
141 split = cmd2.find_first_not_of(" \t", split);
142 std::string args;
143 if(split < cmd2.length())
144 args = cmd2.substr(split);
145 command* cmdh = NULL;
146 if(commands && commands->count(rcmd))
147 cmdh = (*commands)[rcmd];
148 if(!cmdh) {
149 messages << "Unknown command '" << rcmd << "'" << std::endl;
150 return;
152 if(command_stack.count(cmd))
153 throw std::runtime_error("Recursive command invocation");
154 command_stack.insert(cmd);
155 cmdh->invoke(args);
156 command_stack.erase(cmd);
157 return;
158 } catch(std::bad_alloc& e) {
159 OOM_panic();
160 } catch(std::exception& e) {
161 messages << "Error: " << e.what() << std::endl;
162 command_stack.erase(cmd);
163 return;
165 } catch(std::bad_alloc& e) {
166 OOM_panic();
170 std::string command::get_short_help() throw(std::bad_alloc)
172 return "No description available";
175 std::string command::get_long_help() throw(std::bad_alloc)
177 return "No help available on command " + commandname;
180 tokensplitter::tokensplitter(const std::string& _line) throw(std::bad_alloc)
182 line = _line;
183 position = 0;
186 tokensplitter::operator bool() throw()
188 return (position < line.length());
191 tokensplitter::operator std::string() throw(std::bad_alloc)
193 size_t nextp, oldp = position;
194 nextp = line.find_first_of(" \t", position);
195 if(nextp > line.length()) {
196 position = line.length();
197 return line.substr(oldp);
198 } else {
199 position = nextp;
200 while(position < line.length() && (line[position] == ' ' || line[position] == '\t'))
201 position++;
202 return line.substr(oldp, nextp - oldp);
206 std::string tokensplitter::tail() throw(std::bad_alloc)
208 return line.substr(position);
211 template<>
212 void invoke_command_fn(void (*fn)(const std::string& args), const std::string& args)
214 fn(args);
217 template<>
218 void invoke_command_fn(void (*fn)(), const std::string& args)
220 if(args != "")
221 throw std::runtime_error("This command does not take arguments");
222 fn();
225 template<>
226 void invoke_command_fn(void (*fn)(struct arg_filename a), const std::string& args)
228 if(args == "")
229 throw std::runtime_error("Filename required");
230 arg_filename b;
231 b.v = args;
232 fn(b);
235 template<>
236 void invoke_command_fn(void (*fn)(tokensplitter& a), const std::string& args)
238 tokensplitter t(args);
239 fn(t);