monotone

monotone Mtn Source Tree

Root/cmd_automate.cc

1// Copyright (C) 2002, 2008 Graydon Hoare <graydon@pobox.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include <iostream>
12#include <sstream>
13#include <map>
14
15#include "cmd.hh"
16#include "app_state.hh"
17#include "ui.hh"
18#include "lua.hh"
19#include "lua_hooks.hh"
20#include "database.hh"
21#include "work.hh"
22
23using std::istream;
24using std::make_pair;
25using std::map;
26using std::ostream;
27using std::ostringstream;
28using std::pair;
29using std::set;
30using std::string;
31using std::vector;
32
33CMD_GROUP(automate, "automate", "au", CMD_REF(automation),
34 N_("Interface for scripted execution"),
35 "");
36
37namespace commands {
38 automate::automate(string const & name,
39 string const & params,
40 string const & abstract,
41 string const & desc,
42 options::options_type const & opts) :
43 command(name, "", CMD_REF(automate), false, false, params, abstract,
44 // We set use_workspace_options true, because all automate
45 // commands need a database, and they expect to get the database
46 // name from the workspace options, even if they don't need a
47 // workspace for anything else.
48 desc, true, opts, false)
49 {
50 }
51
52 void
53 automate::exec(app_state & app,
54 command_id const & execid,
55 args_vector const & args,
56 std::ostream & output) const
57 {
58 make_io_binary();
59 exec_from_automate(app, execid, args, output);
60 }
61
62 void
63 automate::exec(app_state & app,
64 command_id const & execid,
65 args_vector const & args) const
66 {
67 exec(app, execid, args, std::cout);
68 }
69}
70
71static string const interface_version = "8.1";
72// Major or minor number only increments once for each monotone release;
73// check the most recent release before incrementing this.
74
75// Name: interface_version
76// Arguments: none
77// Added in: 0.0
78// Purpose: Prints version of automation interface. Major number increments
79// whenever a backwards incompatible change is made; minor number increments
80// whenever any change is made (but is reset when major number
81// increments).
82// Output format: "<decimal number>.<decimal number>\n". Always matches
83// "[0-9]+\.[0-9]+\n".
84// Error conditions: None.
85CMD_AUTOMATE(interface_version, "",
86 N_("Prints the automation interface's version"),
87 "",
88 options::opts::none)
89{
90 N(args.empty(),
91 F("no arguments needed"));
92
93 output << interface_version << '\n';
94}
95
96// Name: stdio
97// Arguments: none
98// Added in: 1.0
99// Purpose: Allow multiple automate commands to be run from one instance
100// of monotone.
101//
102// Input format: The input is a series of lines of the form
103// 'l'<size>':'<string>[<size>':'<string>...]'e', with characters
104// after the 'e' of one command, but before the 'l' of the next ignored.
105// This space is reserved, and should not contain characters other
106// than '\n'.
107// Example:
108// l6:leavese
109// l7:parents40:0e3171212f34839c2e3263e7282cdeea22fc5378e
110//
111// Output format: <command number>:<err code>:<last?>:<size>:<output>
112// <command number> is a decimal number specifying which command
113// this output is from. It is 0 for the first command, and increases
114// by one each time.
115// <err code> is 0 for success, 1 for a syntax error, and 2 for any
116// other error.
117// <last?> is 'l' if this is the last piece of output for this command,
118// and 'm' if there is more output to come.
119// <size> is the number of bytes in the output.
120// <output> is the output of the command.
121// Example:
122// 0:0:l:205:0e3171212f34839c2e3263e7282cdeea22fc5378
123// 1f4ef73c3e056883c6a5ff66728dd764557db5e6
124// 2133c52680aa2492b18ed902bdef7e083464c0b8
125// 23501f8afd1f9ee037019765309b0f8428567f8a
126// 2c295fcf5fe20301557b9b3a5b4d437b5ab8ec8c
127// 1:0:l:41:7706a422ccad41621c958affa999b1a1dd644e79
128//
129// Error conditions: Errors encountered by the commands run only set
130// the error code in the output for that command. Malformed input
131// results in exit with a non-zero return value and an error message.
132
133// automate_streambuf and automate_ostream are so we can dump output at a
134// set length, rather than waiting until we have all of the output.
135
136
137class automate_reader
138{
139 istream & in;
140 enum location {opt, cmd, none, eof};
141 location loc;
142 bool get_string(std::string & out)
143 {
144 out.clear();
145 if (loc == none || loc == eof)
146 {
147 return false;
148 }
149 size_t size(0);
150 char c;
151 read(&c, 1);
152 if (c == 'e')
153 {
154 loc = none;
155 return false;
156 }
157 while(c <= '9' && c >= '0')
158 {
159 size = (size*10)+(c-'0');
160 read(&c, 1);
161 }
162 E(c == ':',
163 F("Bad input to automate stdio: expected ':' after string size"));
164 char *str = new char[size];
165 size_t got = 0;
166 while(got < size)
167 {
168 int n = read(str+got, size-got);
169 got += n;
170 }
171 out = std::string(str, size);
172 delete[] str;
173 L(FL("Got string '%s'") % out);
174 return true;
175 }
176 std::streamsize read(char *buf, size_t nbytes, bool eof_ok = false)
177 {
178 std::streamsize rv;
179
180 rv = in.rdbuf()->sgetn(buf, nbytes);
181
182 E(eof_ok || rv > 0, F("Bad input to automate stdio: unexpected EOF"));
183 return rv;
184 }
185 void go_to_next_item()
186 {
187 if (loc == eof)
188 return;
189 string starters("ol");
190 string whitespace(" \r\n\t");
191 string foo;
192 while (loc != none)
193 get_string(foo);
194 char c('e');
195 do
196 {
197 if (read(&c, 1, true) == 0)
198 {
199 loc = eof;
200 return;
201 }
202 }
203 while (whitespace.find(c) != std::string::npos);
204 switch (c)
205 {
206 case 'o': loc = opt; break;
207 case 'l': loc = cmd; break;
208 default:
209 E(false,
210 F("Bad input to automate stdio: unknown start token '%c'") % c);
211 }
212 }
213public:
214 automate_reader(istream & is) : in(is), loc(none)
215 {}
216 bool get_command(vector<pair<string, string> > & params,
217 vector<string> & cmdline)
218 {
219 params.clear();
220 cmdline.clear();
221 if (loc == none)
222 go_to_next_item();
223 if (loc == eof)
224 return false;
225 else if (loc == opt)
226 {
227 string key, val;
228 while(get_string(key) && get_string(val))
229 params.push_back(make_pair(key, val));
230 go_to_next_item();
231 }
232 E(loc == cmd, F("Bad input to automate stdio: expected '%c' token") % cmd);
233 string item;
234 while (get_string(item))
235 {
236 cmdline.push_back(item);
237 }
238 return true;
239 }
240};
241
242struct automate_streambuf : public std::streambuf
243{
244private:
245 size_t _bufsize;
246 std::ostream *out;
247 automate_reader *in;
248 int cmdnum;
249 int err;
250public:
251 automate_streambuf(size_t bufsize)
252 : std::streambuf(), _bufsize(bufsize), out(0), in(0), cmdnum(0), err(0)
253 {
254 char *inbuf = new char[_bufsize];
255 setp(inbuf, inbuf + _bufsize);
256 }
257 automate_streambuf(std::ostream & o, size_t bufsize)
258 : std::streambuf(), _bufsize(bufsize), out(&o), in(0), cmdnum(0), err(0)
259 {
260 char *inbuf = new char[_bufsize];
261 setp(inbuf, inbuf + _bufsize);
262 }
263 automate_streambuf(automate_reader & i, size_t bufsize)
264 : std::streambuf(), _bufsize(bufsize), out(0), in(&i), cmdnum(0), err(0)
265 {
266 char *inbuf = new char[_bufsize];
267 setp(inbuf, inbuf + _bufsize);
268 }
269 ~automate_streambuf()
270 {}
271
272 void set_err(int e)
273 {
274 sync();
275 err = e;
276 }
277
278 void end_cmd()
279 {
280 _M_sync(true);
281 ++cmdnum;
282 err = 0;
283 }
284
285 virtual int sync()
286 {
287 _M_sync();
288 return 0;
289 }
290
291 void _M_sync(bool end = false)
292 {
293 if (!out)
294 {
295 setp(pbase(), pbase() + _bufsize);
296 return;
297 }
298 int num = pptr() - pbase();
299 if (num || end)
300 {
301 (*out) << cmdnum << ':'
302 << err << ':'
303 << (end?'l':'m') << ':'
304 << num << ':' << std::string(pbase(), num);
305 setp(pbase(), pbase() + _bufsize);
306 out->flush();
307 }
308 }
309 int_type
310 overflow(int_type c = traits_type::eof())
311 {
312 sync();
313 sputc(c);
314 return 0;
315 }
316};
317
318struct automate_ostream : public std::ostream
319{
320 automate_streambuf _M_autobuf;
321
322 automate_ostream(std::ostream &out, size_t blocksize)
323 : std::ostream(NULL),
324 _M_autobuf(out, blocksize)
325 { this->init(&_M_autobuf); }
326
327 ~automate_ostream()
328 {}
329
330 automate_streambuf *
331 rdbuf() const
332 { return const_cast<automate_streambuf *>(&_M_autobuf); }
333
334 void set_err(int e)
335 { _M_autobuf.set_err(e); }
336
337 void end_cmd()
338 { _M_autobuf.end_cmd(); }
339};
340
341CMD_AUTOMATE(stdio, "",
342 N_("Automates several commands in one run"),
343 "",
344 options::opts::automate_stdio_size)
345{
346 N(args.empty(),
347 F("no arguments needed"));
348
349 database db(app);
350
351 // initialize the database early so any calling process is notified
352 // immediately if a version discrepancy exists
353 db.ensure_open();
354
355 automate_ostream os(output, app.opts.automate_stdio_size);
356 automate_reader ar(std::cin);
357 vector<pair<string, string> > params;
358 vector<string> cmdline;
359 while(ar.get_command(params, cmdline))//while(!EOF)
360 {
361 args_vector args;
362 vector<string>::iterator i = cmdline.begin();
363 E(i != cmdline.end(),
364 F("Bad input to automate stdio: command name is missing"));
365 for (; i != cmdline.end(); ++i)
366 {
367 args.push_back(arg_type(*i));
368 }
369 try
370 {
371 options::options_type opts;
372 opts = options::opts::all_options() - options::opts::globals();
373 opts.instantiate(&app.opts).reset();
374
375 command_id id;
376 for (args_vector::const_iterator iter = args.begin();
377 iter != args.end(); iter++)
378 id.push_back(utf8((*iter)()));
379
380 if (!id.empty())
381 {
382 I(!args.empty());
383
384 set< command_id > matches =
385 CMD_REF(automate)->complete_command(id);
386
387 if (matches.empty())
388 {
389 N(false, F("no completions for this command"));
390 }
391 else if (matches.size() > 1)
392 {
393 N(false, F("multiple completions possible for this command"));
394 }
395
396 id = *matches.begin();
397
398 I(args.size() >= id.size());
399 for (command_id::size_type i = 0; i < id.size(); i++)
400 args.erase(args.begin());
401
402 command const * cmd = CMD_REF(automate)->find_command(id);
403 I(cmd != NULL);
404 automate const * acmd = reinterpret_cast< automate const * >(cmd);
405
406 opts = options::opts::globals() | acmd->opts();
407
408 if (cmd->use_workspace_options())
409 {
410 // Re-read the ws options file, rather than just copying
411 // the options from the previous apts.opts object, because
412 // the file may have changed due to user activity.
413 workspace::check_ws_format();
414 workspace::get_ws_options(app.opts);
415 }
416
417 opts.instantiate(&app.opts).from_key_value_pairs(params);
418 acmd->exec_from_automate(app, id, args, os);
419 }
420 else
421 opts.instantiate(&app.opts).from_key_value_pairs(params);
422 }
423 catch(informative_failure & f)
424 {
425 os.set_err(2);
426 //Do this instead of printing f.what directly so the output
427 //will be split into properly-sized blocks automatically.
428 os<<f.what();
429 }
430 os.end_cmd();
431 }
432}
433
434LUAEXT(mtn_automate, )
435{
436 args_vector args;
437 std::stringstream output;
438 bool result = true;
439 std::stringstream & os = output;
440
441 try
442 {
443 app_state* app_p = get_app_state(L);
444 I(app_p != NULL);
445 I(app_p->lua.check_lua_state(L));
446 E(app_p->mtn_automate_allowed,
447 F("It is illegal to call the mtn_automate() lua extension,\n"
448 "unless from a command function defined by register_command()."));
449
450 // don't allow recursive calls
451 app_p->mtn_automate_allowed = false;
452
453 // automate_ostream os(output, app_p->opts.automate_stdio_size);
454
455 int n = lua_gettop(L);
456
457 E(n > 0, F("Bad input to mtn_automate() lua extension: command name is missing"));
458
459 L(FL("Starting call to mtn_automate lua hook"));
460
461 for (int i=1; i<=n; i++)
462 {
463 arg_type next_arg(luaL_checkstring(L, i));
464 L(FL("arg: %s")%next_arg());
465 args.push_back(next_arg);
466 }
467
468 commands::command_id id;
469 for (args_vector::const_iterator iter = args.begin();
470 iter != args.end(); iter++)
471 id.push_back(utf8((*iter)()));
472
473 E(!id.empty(), F("no command found"));
474
475 set< commands::command_id > matches =
476 CMD_REF(automate)->complete_command(id);
477
478 if (matches.empty())
479 {
480 N(false, F("no completions for this command"));
481 }
482 else if (matches.size() > 1)
483 {
484 N(false, F("multiple completions possible for this command"));
485 }
486
487 id = *matches.begin();
488
489 I(args.size() >= id.size());
490 for (commands::command_id::size_type i = 0; i < id.size(); i++)
491 args.erase(args.begin());
492
493 commands::command const * cmd = CMD_REF(automate)->find_command(id);
494 I(cmd != NULL);
495 commands::automate const * acmd = reinterpret_cast< commands::automate const * >(cmd);
496
497 acmd->exec(*app_p, id, args, os);
498
499 // allow further calls
500 app_p->mtn_automate_allowed = true;
501 }
502 catch(informative_failure & f)
503 {
504 // informative failures are passed back to the caller
505 result = false;
506 L(FL("Informative failure caught inside lua call to mtn_automate: %s") % f.what());
507 os.flush();
508 output.flush();
509 output.str().clear();
510 os << f.what();
511 }
512 catch (std::logic_error & e)
513 {
514 // invariant failures are permanent
515 result = false;
516 ui.fatal(e.what());
517 lua_pushstring(L, e.what());
518 lua_error(L);
519 }
520
521 os.flush();
522
523 lua_pushboolean(L, result);
524 lua_pushstring(L, output.str().c_str());
525 return 2;
526}
527
528// Local Variables:
529// mode: C++
530// fill-column: 76
531// c-file-style: "gnu"
532// indent-tabs-mode: nil
533// End:
534// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status