1 #include "core/command.hpp"
2 #include "core/globalwrap.hpp"
3 #include "core/misc.hpp"
4 #include "core/zip.hpp"
11 globalwrap
<std::map
<std::string
, command
*>> commands
;
12 std::set
<std::string
> command_stack
;
13 std::map
<std::string
, std::list
<std::string
>> aliases
;
15 function_ptr_command
<arg_filename
> run_script("run-script", "run file as a script",
16 "Syntax: run-script <file>\nRuns file <file> just as it would have been entered in the command line\n",
17 [](arg_filename filename
) throw(std::bad_alloc
, std::runtime_error
) {
18 std::istream
* o
= NULL
;
20 o
= &open_file_relative(filename
, "");
21 messages
<< "Running '" << std::string(filename
) << "'" << std::endl
;
23 while(std::getline(*o
, line
))
24 command::invokeC(line
);
26 } catch(std::exception
& e
) {
33 function_ptr_command
<> show_aliases("show-aliases", "show aliases",
34 "Syntax: show-aliases\nShow expansions of all aliases\n",
35 []() throw(std::bad_alloc
, std::runtime_error
) {
37 for(auto j
= i
.second
.begin(); j
!= i
.second
.end(); j
++)
38 messages
<< "alias " << i
.first
<< " " << *j
<< std::endl
;
41 function_ptr_command
<tokensplitter
&> unalias_command("unalias-command", "unalias a command",
42 "Syntax: unalias-command <aliasname>\nClear expansion of alias <aliasname>\n",
43 [](tokensplitter
& t
) throw(std::bad_alloc
, std::runtime_error
) {
44 std::string aliasname
= t
;
46 throw std::runtime_error("This command only takes one argument");
47 if(aliasname
.length() == 0 || aliasname
[0] == '?' || aliasname
[0] == '*')
48 throw std::runtime_error("Illegal alias name");
49 aliases
[aliasname
].clear();
50 messages
<< "Command '" << aliasname
<< "' unaliased" << std::endl
;
53 function_ptr_command
<tokensplitter
&> alias_command("alias-command", "alias a command",
54 "Syntax: alias-command <aliasname> <command>\nAppend <command> to expansion of alias <aliasname>\n"
55 "Valid alias names can't be empty nor start with '*' or '?'\n",
56 [](tokensplitter
& t
) throw(std::bad_alloc
, std::runtime_error
) {
57 std::string aliasname
= t
;
58 std::string command
= t
.tail();
60 throw std::runtime_error("Alias name and command needed");
61 if(aliasname
.length() == 0 || aliasname
[0] == '?' || aliasname
[0] == '*')
62 throw std::runtime_error("Illegal alias name");
63 aliases
[aliasname
].push_back(command
);
64 messages
<< "Command '" << aliasname
<< "' aliased to '" << command
<< "'" << std::endl
;
68 command::command(const std::string
& cmd
) throw(std::bad_alloc
)
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 commands().erase(commandname
);
80 void command::invokeC(const std::string
& cmd
) throw()
83 std::string cmd2
= cmd
;
84 if(cmd2
[cmd2
.length() - 1] == '\r')
85 cmd2
= cmd2
.substr(0, cmd2
.length() - 1);
87 //The special ? command.
88 for(auto i
: commands())
89 messages
<< i
.first
<< ": " << i
.second
->get_short_help() << std::endl
;
92 if(cmd2
.length() > 1 && cmd2
[0] == '?') {
94 size_t split
= cmd2
.find_first_of(" \t");
96 if(split
>= cmd2
.length())
97 rcmd
= cmd2
.substr(1);
99 rcmd
= cmd2
.substr(1, split
- 1);
100 if(rcmd
.length() > 0 && rcmd
[0] != '*') {
101 //This may be an alias.
102 std::string aname
= cmd2
.substr(1);
103 if(aliases
.count(aname
)) {
105 messages
<< aname
<< " is an alias for: " << std::endl
;
107 for(auto i
: aliases
[aname
])
108 messages
<< "#" + (++j
) << ": " << i
<< std::endl
;
112 if(rcmd
.length() > 0 && rcmd
[0] == '*')
113 rcmd
= rcmd
.substr(1);
114 if(!commands().count(rcmd
)) {
116 messages
<< "Unknown command '" << rcmd
<< "'" << std::endl
;
119 messages
<< commands()[rcmd
]->get_long_help() << std::endl
;
122 bool may_be_alias_expanded
= true;
123 if(cmd2
.length() > 0 && cmd2
[0] == '*') {
124 may_be_alias_expanded
= false;
125 cmd2
= cmd2
.substr(1);
127 if(may_be_alias_expanded
&& aliases
.count(cmd2
)) {
128 for(auto i
: aliases
[cmd2
])
133 size_t split
= cmd2
.find_first_of(" \t");
135 if(split
>= cmd2
.length())
138 rcmd
= cmd2
.substr(0, split
);
139 split
= cmd2
.find_first_not_of(" \t", split
);
141 if(split
< cmd2
.length())
142 args
= cmd2
.substr(split
);
143 command
* cmdh
= NULL
;
144 if(commands().count(rcmd
))
145 cmdh
= commands()[rcmd
];
147 messages
<< "Unknown command '" << rcmd
<< "'" << std::endl
;
150 if(command_stack
.count(cmd2
))
151 throw std::runtime_error("Recursive command invocation");
152 command_stack
.insert(cmd2
);
154 command_stack
.erase(cmd2
);
156 } catch(std::bad_alloc
& e
) {
158 } catch(std::exception
& e
) {
159 messages
<< "Error: " << e
.what() << std::endl
;
160 command_stack
.erase(cmd2
);
163 } catch(std::bad_alloc
& e
) {
168 std::string
command::get_short_help() throw(std::bad_alloc
)
170 return "No description available";
173 std::string
command::get_long_help() throw(std::bad_alloc
)
175 return "No help available on command " + commandname
;
178 std::set
<std::string
> command::get_aliases() throw(std::bad_alloc
)
180 std::set
<std::string
> r
;
181 for(auto i
: aliases
)
186 std::string
command::get_alias_for(const std::string
& aname
) throw(std::bad_alloc
)
188 if(!valid_alias_name(aname
))
190 if(aliases
.count(aname
)) {
192 for(auto i
: aliases
[aname
])
199 void command::set_alias_for(const std::string
& aname
, const std::string
& avalue
) throw(std::bad_alloc
)
201 if(!valid_alias_name(aname
))
203 std::list
<std::string
> newlist
;
205 while(avitr
< avalue
.length()) {
206 size_t nextsplit
= avalue
.find_first_of("\n", avitr
);
207 if(nextsplit
>= avalue
.length())
208 nextsplit
= avalue
.length();
209 std::string x
= avalue
.substr(avitr
, nextsplit
- avitr
);
210 if(x
.length() > 0 && x
[x
.length() - 1] == '\r')
211 x
= x
.substr(0, x
.length() - 1);
213 newlist
.push_back(x
);
214 avitr
= nextsplit
+ 1;
217 aliases
.erase(aname
);
219 aliases
[aname
] = newlist
;
222 bool command::valid_alias_name(const std::string
& aliasname
) throw(std::bad_alloc
)
224 if(aliasname
.length() == 0 || aliasname
[0] == '?' || aliasname
[0] == '*')
226 if(aliasname
.find_first_of(" \t") < aliasname
.length())
232 tokensplitter::tokensplitter(const std::string
& _line
) throw(std::bad_alloc
)
238 tokensplitter::operator bool() throw()
240 return (position
< line
.length());
243 tokensplitter::operator std::string() throw(std::bad_alloc
)
245 size_t nextp
, oldp
= position
;
246 nextp
= line
.find_first_of(" \t", position
);
247 if(nextp
> line
.length()) {
248 position
= line
.length();
249 return line
.substr(oldp
);
252 while(position
< line
.length() && (line
[position
] == ' ' || line
[position
] == '\t'))
254 return line
.substr(oldp
, nextp
- oldp
);
258 std::string
tokensplitter::tail() throw(std::bad_alloc
)
260 return line
.substr(position
);
264 void invoke_command_fn(void (*fn
)(const std::string
& args
), const std::string
& args
)
270 void invoke_command_fn(void (*fn
)(), const std::string
& args
)
273 throw std::runtime_error("This command does not take arguments");
278 void invoke_command_fn(void (*fn
)(struct arg_filename a
), const std::string
& args
)
281 throw std::runtime_error("Filename required");
288 void invoke_command_fn(void (*fn
)(tokensplitter
& a
), const std::string
& args
)
290 tokensplitter
t(args
);