Describe command changes from last commit
[lsnes.git] / command.cpp
blob7e6fcecad7475a594140aa5ed18ad0aa09c615ab
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 class run_command : public command
16 public:
17 run_command() throw(std::bad_alloc) : command("run-script") {}
18 void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
20 if(args == "")
21 throw std::runtime_error("Filename needed");
22 std::istream* o = NULL;
23 try {
24 o = &open_file_relative(args, "");
25 out(win) << "Running '" << args << "'" << std::endl;
26 std::string line;
27 while(std::getline(*o, line))
28 command::invokeC(line, win);
29 delete o;
30 } catch(std::exception& e) {
31 if(o)
32 delete o;
33 throw;
36 std::string get_short_help() throw(std::bad_alloc) { return "run a file as a script"; }
37 std::string get_long_help() throw(std::bad_alloc)
39 return "Syntax: run-script <file>\n"
40 "Runs file <file> just as it would have been entered in the command line\n";
42 } run;
44 class aliases_command : public command
46 public:
47 aliases_command() throw(std::bad_alloc) : command("show-aliases") {}
48 void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
50 if(args != "")
51 throw std::runtime_error("This command does not take parameters");
52 for(auto i = aliases.begin(); i != aliases.end(); i++)
53 for(auto j = i->second.begin(); j != i->second.end(); j++)
54 out(win) << "alias " << i->first << " " << *j << std::endl;
56 std::string get_short_help() throw(std::bad_alloc) { return "show aliases"; }
57 std::string get_long_help() throw(std::bad_alloc)
59 return "Syntax: show-aliases\n"
60 "Show expansions of all aliases\n";
62 } sh_aliases;
64 class unalias_command : public command
66 public:
67 unalias_command() throw(std::bad_alloc) : command("unalias-command") {}
68 void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
70 tokensplitter t(args);
71 std::string aliasname = t;
72 if(t)
73 throw std::runtime_error("This command only takes one argument");
74 if(aliasname.length() == 0 || aliasname[0] == '?' || aliasname[0] == '*')
75 throw std::runtime_error("Illegal alias name");
76 aliases[aliasname].clear();
77 out(win) << "Command '" << aliasname << "' unaliased" << std::endl;
79 std::string get_short_help() throw(std::bad_alloc) { return "unalias a command"; }
80 std::string get_long_help() throw(std::bad_alloc)
82 return "Syntax: unalias-command <aliasname>\n"
83 "Clear expansion of alias <aliasname>\n";
85 } unalias;
87 class alias_command : public command
89 public:
90 alias_command() throw(std::bad_alloc) : command("alias-command") {}
91 void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
93 tokensplitter t(args);
94 std::string aliasname = t;
95 std::string command = t.tail();
96 if(command == "")
97 throw std::runtime_error("Alias name and command needed");
98 if(aliasname.length() == 0 || aliasname[0] == '?' || aliasname[0] == '*')
99 throw std::runtime_error("Illegal alias name");
100 aliases[aliasname].push_back(command);
101 out(win) << "Command '" << aliasname << "' aliased to '" << command << "'" << std::endl;
103 std::string get_short_help() throw(std::bad_alloc) { return "alias a command"; }
104 std::string get_long_help() throw(std::bad_alloc)
106 return "Syntax: alias-command <aliasname> <command>\n"
107 "Append <command> to expansion of alias <aliasname>\n"
108 "Valid alias names can't be empty nor start with '*' or '?'\n";
110 } alias;
113 command::command(const std::string& cmd) throw(std::bad_alloc)
115 if(!commands)
116 commands = new std::map<std::string, command*>();
117 if(commands->count(cmd))
118 std::cerr << "WARNING: Command collision for " << cmd << "!" << std::endl;
119 (*commands)[commandname = cmd] = this;
122 command::~command() throw()
124 if(!commands)
125 return;
126 commands->erase(commandname);
129 void command::invokeC(const std::string& cmd, window* win) throw()
131 try {
132 if(command_stack.count(cmd)) {
133 out(win) << "Can not invoke recursively: " << cmd << std::endl;
134 return;
136 command_stack.insert(cmd);
137 std::string cmd2 = cmd;
138 if(cmd2 == "?") {
139 //The special ? command.
140 if(commands) {
141 for(auto i = commands->begin(); i != commands->end(); ++i)
142 out(win) << i->first << ": " << i->second->get_short_help() << std::endl;
144 command_stack.erase(cmd);
145 return;
147 if(cmd2.length() > 1 && cmd2[0] == '?') {
148 //?command.
149 size_t split = cmd2.find_first_of(" \t");
150 std::string rcmd;
151 if(split >= cmd2.length())
152 rcmd = cmd2.substr(1);
153 else
154 rcmd = cmd2.substr(1, split - 1);
155 if(rcmd.length() > 0 && rcmd[0] != '*') {
156 //This may be an alias.
157 std::string aname = cmd2.substr(1);
158 if(aliases.count(aname)) {
159 //Yup.
160 out(win) << aname << " is an alias for: " << std::endl;
161 size_t j = 0;
162 for(auto i = aliases[aname].begin(); i != aliases[aname].end(); ++i, ++j)
163 out(win) << "#" + (j + 1) << ": " << *i << std::endl;
164 command_stack.erase(cmd);
165 return;
168 if(rcmd.length() > 0 && rcmd[0] == '*')
169 rcmd = rcmd.substr(1);
170 if(!commands || !commands->count(rcmd)) {
171 if(rcmd != "")
172 out(win) << "Unknown command '" << rcmd << "'" << std::endl;
173 command_stack.erase(cmd);
174 return;
176 out(win) << (*commands)[rcmd]->get_long_help() << std::endl;
177 command_stack.erase(cmd);
178 return;
180 bool may_be_alias_expanded = true;
181 if(cmd2.length() > 0 && cmd2[0] == '*') {
182 may_be_alias_expanded = false;
183 cmd2 = cmd2.substr(1);
185 if(may_be_alias_expanded && aliases.count(cmd2)) {
186 for(auto i = aliases[cmd2].begin(); i != aliases[cmd2].end(); ++i)
187 invokeC(*i, win);
188 command_stack.erase(cmd);
189 return;
191 try {
192 size_t split = cmd2.find_first_of(" \t");
193 std::string rcmd;
194 if(split >= cmd2.length())
195 rcmd = cmd2;
196 else
197 rcmd = cmd2.substr(0, split);
198 split = cmd2.find_first_not_of(" \t", split);
199 std::string args;
200 if(split < cmd2.length())
201 args = cmd2.substr(split);
202 command* cmdh = NULL;
203 if(commands && commands->count(rcmd))
204 cmdh = (*commands)[rcmd];
205 if(!cmdh) {
206 out(win) << "Unknown command '" << rcmd << "'" << std::endl;
207 command_stack.erase(cmd);
208 return;
210 cmdh->invoke(args, win);
211 command_stack.erase(cmd);
212 return;
213 } catch(std::bad_alloc& e) {
214 OOM_panic(win);
215 } catch(std::exception& e) {
216 out(win) << "Error: " << e.what() << std::endl;
217 command_stack.erase(cmd);
218 return;
220 } catch(std::bad_alloc& e) {
221 OOM_panic(win);
225 std::string command::get_short_help() throw(std::bad_alloc)
227 return "No description available";
230 std::string command::get_long_help() throw(std::bad_alloc)
232 return "No help available on command " + commandname;
235 tokensplitter::tokensplitter(const std::string& _line) throw(std::bad_alloc)
237 line = _line;
238 position = 0;
241 tokensplitter::operator bool() throw()
243 return (position < line.length());
246 tokensplitter::operator std::string() throw(std::bad_alloc)
248 size_t nextp, oldp = position;
249 nextp = line.find_first_of(" \t", position);
250 if(nextp > line.length()) {
251 position = line.length();
252 return line.substr(oldp);
253 } else {
254 position = nextp;
255 while(position < line.length() && (line[position] == ' ' || line[position] == '\t'))
256 position++;
257 return line.substr(oldp, nextp - oldp);
261 std::string tokensplitter::tail() throw(std::bad_alloc)
263 return line.substr(position);