Since window is singleton anyway, get rid of window* parameters
[lsnes.git] / command.cpp
blob3845a7cdff97cdbdb325b5345c281afee4ecc99a
1 #include "command.hpp"
2 #include "misc.hpp"
3 #include "zip.hpp"
4 #include "window.hpp"
5 #include <set>
6 #include <map>
8 namespace
10 std::map<std::string, command*>* commands;
11 std::set<std::string> command_stack;
12 std::map<std::string, std::list<std::string>> aliases;
14 function_ptr_command run_script("run-script", "run file as a script",
15 "Syntax: run-script <file>\nRuns file <file> just as it would have been entered in the command line\n",
16 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
17 if(args == "")
18 throw std::runtime_error("Filename needed");
19 std::istream* o = NULL;
20 try {
21 o = &open_file_relative(args, "");
22 window::out() << "Running '" << args << "'" << std::endl;
23 std::string line;
24 while(std::getline(*o, line))
25 command::invokeC(line);
26 delete o;
27 } catch(std::exception& e) {
28 if(o)
29 delete o;
30 throw;
32 });
34 function_ptr_command show_aliases("show-aliases", "show aliases",
35 "Syntax: show-aliases\nShow expansions of all aliases\n",
36 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
37 if(args != "")
38 throw std::runtime_error("This command does not take parameters");
39 for(auto i = aliases.begin(); i != aliases.end(); i++)
40 for(auto j = i->second.begin(); j != i->second.end(); j++)
41 window::out() << "alias " << i->first << " " << *j << std::endl;
42 });
44 function_ptr_command unalias_command("unalias-command", "unalias a command",
45 "Syntax: unalias-command <aliasname>\nClear expansion of alias <aliasname>\n",
46 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
47 tokensplitter t(args);
48 std::string aliasname = t;
49 if(t)
50 throw std::runtime_error("This command only takes one argument");
51 if(aliasname.length() == 0 || aliasname[0] == '?' || aliasname[0] == '*')
52 throw std::runtime_error("Illegal alias name");
53 aliases[aliasname].clear();
54 window::out() << "Command '" << aliasname << "' unaliased" << std::endl;
55 });
57 function_ptr_command alias_command("alias-command", "alias a command",
58 "Syntax: alias-command <aliasname> <command>\nAppend <command> to expansion of alias <aliasname>\n"
59 "Valid alias names can't be empty nor start with '*' or '?'\n",
60 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
61 tokensplitter t(args);
62 std::string aliasname = t;
63 std::string command = t.tail();
64 if(command == "")
65 throw std::runtime_error("Alias name and command needed");
66 if(aliasname.length() == 0 || aliasname[0] == '?' || aliasname[0] == '*')
67 throw std::runtime_error("Illegal alias name");
68 aliases[aliasname].push_back(command);
69 window::out() << "Command '" << aliasname << "' aliased to '" << command << "'" << std::endl;
70 });
73 command::command(const std::string& cmd) throw(std::bad_alloc)
75 if(!commands)
76 commands = new std::map<std::string, command*>();
77 if(commands->count(cmd))
78 std::cerr << "WARNING: Command collision for " << cmd << "!" << std::endl;
79 (*commands)[commandname = cmd] = this;
82 command::~command() throw()
84 if(!commands)
85 return;
86 commands->erase(commandname);
89 void command::invokeC(const std::string& cmd) throw()
91 try {
92 if(command_stack.count(cmd)) {
93 window::out() << "Can not invoke recursively: " << cmd << std::endl;
94 return;
96 command_stack.insert(cmd);
97 std::string cmd2 = cmd;
98 if(cmd2 == "?") {
99 //The special ? command.
100 if(commands) {
101 for(auto i = commands->begin(); i != commands->end(); ++i)
102 window::out() << i->first << ": " << i->second->get_short_help() << std::endl;
104 command_stack.erase(cmd);
105 return;
107 if(cmd2.length() > 1 && cmd2[0] == '?') {
108 //?command.
109 size_t split = cmd2.find_first_of(" \t");
110 std::string rcmd;
111 if(split >= cmd2.length())
112 rcmd = cmd2.substr(1);
113 else
114 rcmd = cmd2.substr(1, split - 1);
115 if(rcmd.length() > 0 && rcmd[0] != '*') {
116 //This may be an alias.
117 std::string aname = cmd2.substr(1);
118 if(aliases.count(aname)) {
119 //Yup.
120 window::out() << aname << " is an alias for: " << std::endl;
121 size_t j = 0;
122 for(auto i = aliases[aname].begin(); i != aliases[aname].end(); ++i, ++j)
123 window::out() << "#" + (j + 1) << ": " << *i << std::endl;
124 command_stack.erase(cmd);
125 return;
128 if(rcmd.length() > 0 && rcmd[0] == '*')
129 rcmd = rcmd.substr(1);
130 if(!commands || !commands->count(rcmd)) {
131 if(rcmd != "")
132 window::out() << "Unknown command '" << rcmd << "'" << std::endl;
133 command_stack.erase(cmd);
134 return;
136 window::out() << (*commands)[rcmd]->get_long_help() << std::endl;
137 command_stack.erase(cmd);
138 return;
140 bool may_be_alias_expanded = true;
141 if(cmd2.length() > 0 && cmd2[0] == '*') {
142 may_be_alias_expanded = false;
143 cmd2 = cmd2.substr(1);
145 if(may_be_alias_expanded && aliases.count(cmd2)) {
146 for(auto i = aliases[cmd2].begin(); i != aliases[cmd2].end(); ++i)
147 invokeC(*i);
148 command_stack.erase(cmd);
149 return;
151 try {
152 size_t split = cmd2.find_first_of(" \t");
153 std::string rcmd;
154 if(split >= cmd2.length())
155 rcmd = cmd2;
156 else
157 rcmd = cmd2.substr(0, split);
158 split = cmd2.find_first_not_of(" \t", split);
159 std::string args;
160 if(split < cmd2.length())
161 args = cmd2.substr(split);
162 command* cmdh = NULL;
163 if(commands && commands->count(rcmd))
164 cmdh = (*commands)[rcmd];
165 if(!cmdh) {
166 window::out() << "Unknown command '" << rcmd << "'" << std::endl;
167 command_stack.erase(cmd);
168 return;
170 cmdh->invoke(args);
171 command_stack.erase(cmd);
172 return;
173 } catch(std::bad_alloc& e) {
174 OOM_panic();
175 } catch(std::exception& e) {
176 window::out() << "Error: " << e.what() << std::endl;
177 command_stack.erase(cmd);
178 return;
180 } catch(std::bad_alloc& e) {
181 OOM_panic();
185 std::string command::get_short_help() throw(std::bad_alloc)
187 return "No description available";
190 std::string command::get_long_help() throw(std::bad_alloc)
192 return "No help available on command " + commandname;
195 tokensplitter::tokensplitter(const std::string& _line) throw(std::bad_alloc)
197 line = _line;
198 position = 0;
201 tokensplitter::operator bool() throw()
203 return (position < line.length());
206 tokensplitter::operator std::string() throw(std::bad_alloc)
208 size_t nextp, oldp = position;
209 nextp = line.find_first_of(" \t", position);
210 if(nextp > line.length()) {
211 position = line.length();
212 return line.substr(oldp);
213 } else {
214 position = nextp;
215 while(position < line.length() && (line[position] == ' ' || line[position] == '\t'))
216 position++;
217 return line.substr(oldp, nextp - oldp);
221 std::string tokensplitter::tail() throw(std::bad_alloc)
223 return line.substr(position);
226 function_ptr_command::function_ptr_command(const std::string& name, const std::string& _description,
227 const std::string& _help, void (*_fn)(const std::string& arguments)) throw(std::bad_alloc)
228 : command(name)
230 description = _description;
231 help = _help;
232 fn = _fn;
235 void function_ptr_command::invoke(const std::string& args) throw(std::bad_alloc, std::runtime_error)
237 fn(args);
240 std::string function_ptr_command::get_short_help() throw(std::bad_alloc)
242 return description;
245 std::string function_ptr_command::get_long_help() throw(std::bad_alloc)
247 return help;
250 function_ptr_command::~function_ptr_command() throw()