Some tweaks to Lua docs
[lsnes.git] / src / library / command.cpp
blob2bb9e804d7584e416b6d6962b67935f48cef9e0e
1 #include "command.hpp"
2 #include "globalwrap.hpp"
3 #include "minmax.hpp"
4 #include "register-queue.hpp"
5 #include "string.hpp"
6 #include "zip.hpp"
7 #include <iostream>
8 #include <cstdlib>
10 namespace command
12 namespace
14 struct run_script : public base
16 run_script(group& group, std::ostream*& _output)
17 : base(group, "run-script"), in_group(group), output(_output)
21 ~run_script() throw()
25 void invoke(const std::string& filename) throw(std::bad_alloc, std::runtime_error)
27 if(filename == "") {
28 (*output) << "Syntax: run-script <scriptfile>" << std::endl;
29 return;
31 std::istream* o = NULL;
32 try {
33 o = &zip::openrel(filename, "");
34 (*output) << "Running '" << std::string(filename) << "'" << std::endl;
35 std::string line;
36 while(std::getline(*o, line))
37 in_group.invoke(line);
38 delete o;
39 } catch(std::exception& e) {
40 delete o;
41 throw;
45 std::string get_short_help() throw(std::bad_alloc)
47 return "Run file as a script";
50 std::string get_long_help() throw(std::bad_alloc)
52 return "Syntax: run-script <file>\nRuns file <file> just as it would have been entered in "
53 "the command line\n";
56 group& in_group;
57 std::ostream*& output;
60 void default_oom_panic()
62 std::cerr << "PANIC: Fatal error, can't continue: Out of memory." << std::endl;
63 exit(1);
66 typedef register_queue<group, base> regqueue_t;
69 base::base(group& group, const std::string& cmd) throw(std::bad_alloc)
70 : in_group(group)
72 regqueue_t::do_register(in_group, commandname = cmd, *this);
75 base::~base() throw()
77 regqueue_t::do_unregister(in_group, commandname);
81 std::string base::get_short_help() throw(std::bad_alloc)
83 return "No description available";
86 std::string base::get_long_help() throw(std::bad_alloc)
88 return "No help available on command " + commandname;
91 group::group() throw(std::bad_alloc)
93 oom_panic_routine = default_oom_panic;
94 output = &std::cerr;
95 regqueue_t::do_ready(*this, true);
96 //The builtin commands.
97 builtin[0] = new run_script(*this, output);
100 group::~group() throw()
102 for(size_t i = 0; i < sizeof(builtin)/sizeof(builtin[0]); i++)
103 delete builtin[i];
104 regqueue_t::do_ready(*this, false);
107 void group::invoke(const std::string& cmd) throw()
109 try {
110 std::string cmd2 = strip_CR(cmd);
111 if(cmd2 == "?") {
112 //The special ? command.
113 umutex_class lock(int_mutex);
114 for(auto i : commands)
115 (*output) << i.first << ": " << i.second->get_short_help() << std::endl;
116 return;
118 if(firstchar(cmd2) == '?') {
119 //?command.
120 umutex_class lock(int_mutex);
121 std::string rcmd = cmd2.substr(1, min(cmd2.find_first_of(" \t"), cmd2.length()));
122 if(firstchar(rcmd) != '*') {
123 //This may be an alias.
124 if(aliases.count(rcmd)) {
125 //Yup.
126 (*output) << rcmd << " is an alias for: " << std::endl;
127 size_t j = 0;
128 for(auto i : aliases[rcmd])
129 (*output) << "#" << (++j) << ": " << i << std::endl;
130 return;
132 } else
133 rcmd = rcmd.substr(1);
134 if(!commands.count(rcmd))
135 (*output) << "Unknown command '" << rcmd << "'" << std::endl;
136 else
137 (*output) << commands[rcmd]->get_long_help() << std::endl;
138 return;
140 bool may_be_alias_expanded = true;
141 if(firstchar(cmd2) == '*') {
142 may_be_alias_expanded = false;
143 cmd2 = cmd2.substr(1);
145 //Now this gets painful as command handlers must not be invoked with lock held.
146 if(may_be_alias_expanded) {
147 std::list<std::string> aexp;
149 umutex_class lock(int_mutex);
150 if(!aliases.count(cmd))
151 goto not_alias;
152 aexp = aliases[cmd2];
154 for(auto i : aexp)
155 invoke(i);
156 return;
158 not_alias:
159 try {
160 size_t split = cmd2.find_first_of(" \t");
161 std::string rcmd = cmd2.substr(0, min(split, cmd2.length()));
162 std::string args = cmd2.substr(min(cmd2.find_first_not_of(" \t", split), cmd2.length()));
163 base* cmdh = NULL;
165 umutex_class lock(int_mutex);
166 if(!commands.count(rcmd)) {
167 (*output) << "Unknown command '" << rcmd << "'" << std::endl;
168 return;
170 cmdh = commands[rcmd];
172 if(command_stack.count(cmd2))
173 throw std::runtime_error("Recursive command invocation");
174 command_stack.insert(cmd2);
175 cmdh->invoke(args);
176 command_stack.erase(cmd2);
177 return;
178 } catch(std::bad_alloc& e) {
179 oom_panic_routine();
180 } catch(std::exception& e) {
181 (*output) << "Error: " << e.what() << std::endl;
182 command_stack.erase(cmd2);
183 return;
185 } catch(std::bad_alloc& e) {
186 oom_panic_routine();
190 std::set<std::string> group::get_aliases() throw(std::bad_alloc)
192 umutex_class lock(int_mutex);
193 std::set<std::string> r;
194 for(auto i : aliases)
195 r.insert(i.first);
196 return r;
199 std::string group::get_alias_for(const std::string& aname) throw(std::bad_alloc)
201 umutex_class lock(int_mutex);
202 if(!valid_alias_name(aname))
203 return "";
204 if(aliases.count(aname)) {
205 std::string x;
206 for(auto i : aliases[aname])
207 x = x + i + "\n";
208 return x;
209 } else
210 return "";
213 void group::set_alias_for(const std::string& aname, const std::string& avalue) throw(std::bad_alloc)
215 umutex_class lock(int_mutex);
216 if(!valid_alias_name(aname))
217 return;
218 std::list<std::string> newlist;
219 size_t avitr = 0;
220 while(avitr < avalue.length()) {
221 size_t nextsplit = min(avalue.find_first_of("\n", avitr), avalue.length());
222 std::string x = strip_CR(avalue.substr(avitr, nextsplit - avitr));
223 if(x.length() > 0)
224 newlist.push_back(x);
225 avitr = nextsplit + 1;
227 if(newlist.empty())
228 aliases.erase(aname);
229 else
230 aliases[aname] = newlist;
233 bool group::valid_alias_name(const std::string& aliasname) throw(std::bad_alloc)
235 if(aliasname.length() == 0 || aliasname[0] == '?' || aliasname[0] == '*')
236 return false;
237 if(aliasname.find_first_of(" \t") < aliasname.length())
238 return false;
239 return true;
242 void group::do_register(const std::string& name, base& cmd) throw(std::bad_alloc)
244 umutex_class lock(int_mutex);
245 if(commands.count(name))
246 std::cerr << "WARNING: Command collision for " << name << "!" << std::endl;
247 commands[name] = &cmd;
250 void group::do_unregister(const std::string& name) throw(std::bad_alloc)
252 umutex_class lock(int_mutex);
253 commands.erase(name);
256 void group::set_output(std::ostream& s)
258 output = &s;
261 void group::set_oom_panic(void (*fn)())
263 if(fn)
264 oom_panic_routine = fn;
265 else
266 oom_panic_routine = default_oom_panic;
269 template<>
270 void invoke_fn(void (*fn)(const std::string& args), const std::string& args)
272 fn(args);
275 template<>
276 void invoke_fn(void (*fn)(), const std::string& args)
278 if(args != "")
279 throw std::runtime_error("This command does not take arguments");
280 fn();
283 template<>
284 void invoke_fn(void (*fn)(struct arg_filename a), const std::string& args)
286 if(args == "")
287 throw std::runtime_error("Filename required");
288 arg_filename b;
289 b.v = args;
290 fn(b);