monotone

monotone Mtn Source Tree

Root/cmd_automate.cc

1// Copyright (C) 2002 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 <map>
13
14#include "cmd.hh"
15#include "app_state.hh"
16
17using std::istream;
18using std::make_pair;
19using std::map;
20using std::ostream;
21using std::pair;
22using std::set;
23using std::string;
24using std::vector;
25
26CMD_GROUP(automate, "automate", "au", CMD_REF(automation),
27 N_("Interface for scripted execution"),
28 "");
29
30namespace commands {
31 automate::automate(string const & name,
32 string const & params,
33 string const & abstract,
34 string const & desc,
35 options::options_type const & opts) :
36 command(name, "", CMD_REF(automate), false, params, abstract,
37 desc, true, opts, false)
38 {
39 }
40
41 void
42 automate::exec(app_state & app,
43 command_id const & execid,
44 args_vector const & args) const
45 {
46 make_io_binary();
47 exec_from_automate(args, execid, app, std::cout);
48 }
49}
50
51static string const interface_version = "5.0";
52
53// Name: interface_version
54// Arguments: none
55// Added in: 0.0
56// Purpose: Prints version of automation interface. Major number increments
57// whenever a backwards incompatible change is made; minor number increments
58// whenever any change is made (but is reset when major number increments).
59// Output format: "<decimal number>.<decimal number>\n". Always matches
60// "[0-9]+\.[0-9]+\n".
61// Error conditions: None.
62CMD_AUTOMATE(interface_version, "",
63 N_("Prints the automation interface's version"),
64 "",
65 options::opts::none)
66{
67 N(args.size() == 0,
68 F("no arguments needed"));
69
70 output << interface_version << '\n';
71}
72
73// Name: stdio
74// Arguments: none
75// Added in: 1.0
76// Purpose: Allow multiple automate commands to be run from one instance
77// of monotone.
78//
79// Input format: The input is a series of lines of the form
80// 'l'<size>':'<string>[<size>':'<string>...]'e', with characters
81// after the 'e' of one command, but before the 'l' of the next ignored.
82// This space is reserved, and should not contain characters other
83// than '\n'.
84// Example:
85// l6:leavese
86// l7:parents40:0e3171212f34839c2e3263e7282cdeea22fc5378e
87//
88// Output format: <command number>:<err code>:<last?>:<size>:<output>
89// <command number> is a decimal number specifying which command
90// this output is from. It is 0 for the first command, and increases
91// by one each time.
92// <err code> is 0 for success, 1 for a syntax error, and 2 for any
93// other error.
94// <last?> is 'l' if this is the last piece of output for this command,
95// and 'm' if there is more output to come.
96// <size> is the number of bytes in the output.
97// <output> is the output of the command.
98// Example:
99// 0:0:l:205:0e3171212f34839c2e3263e7282cdeea22fc5378
100// 1f4ef73c3e056883c6a5ff66728dd764557db5e6
101// 2133c52680aa2492b18ed902bdef7e083464c0b8
102// 23501f8afd1f9ee037019765309b0f8428567f8a
103// 2c295fcf5fe20301557b9b3a5b4d437b5ab8ec8c
104// 1:0:l:41:7706a422ccad41621c958affa999b1a1dd644e79
105//
106// Error conditions: Errors encountered by the commands run only set
107// the error code in the output for that command. Malformed input
108// results in exit with a non-zero return value and an error message.
109
110// automate_streambuf and automate_ostream are so we can dump output at a
111// set length, rather than waiting until we have all of the output.
112
113
114class automate_reader
115{
116 istream & in;
117 enum location {opt, cmd, none, eof};
118 location loc;
119 bool get_string(std::string & out)
120 {
121 out.clear();
122 if (loc == none || loc == eof)
123 {
124 return false;
125 }
126 size_t size(0);
127 char c;
128 read(&c, 1);
129 if (c == 'e')
130 {
131 loc = none;
132 return false;
133 }
134 while(c <= '9' && c >= '0')
135 {
136 size = (size*10)+(c-'0');
137 read(&c, 1);
138 }
139 E(c == ':',
140 F("Bad input to automate stdio: expected ':' after string size"));
141 char *str = new char[size];
142 size_t got = 0;
143 while(got < size)
144 {
145 int n = read(str+got, size-got);
146 got += n;
147 }
148 out = std::string(str, size);
149 delete[] str;
150 L(FL("Got string '%s'") % out);
151 return true;
152 }
153 std::streamsize read(char *buf, size_t nbytes, bool eof_ok = false)
154 {
155 std::streamsize rv;
156
157 rv = in.rdbuf()->sgetn(buf, nbytes);
158
159 E(eof_ok || rv > 0, F("Bad input to automate stdio: unexpected EOF"));
160 return rv;
161 }
162 void go_to_next_item()
163 {
164 if (loc == eof)
165 return;
166 string starters("ol");
167 string whitespace(" \r\n\t");
168 string foo;
169 while (loc != none)
170 get_string(foo);
171 char c('e');
172 do
173 {
174 if (read(&c, 1, true) == 0)
175 {
176 loc = eof;
177 return;
178 }
179 }
180 while (whitespace.find(c) != std::string::npos);
181 switch (c)
182 {
183 case 'o': loc = opt; break;
184 case 'l': loc = cmd; break;
185 default:
186 E(false,
187 F("Bad input to automate stdio: unknown start token '%c'") % c);
188 }
189 }
190public:
191 automate_reader(istream & is) : in(is), loc(none)
192 {}
193 bool get_command(vector<pair<string, string> > & params,
194 vector<string> & cmdline)
195 {
196 params.clear();
197 cmdline.clear();
198 if (loc == none)
199 go_to_next_item();
200 if (loc == eof)
201 return false;
202 else if (loc == opt)
203 {
204 string key, val;
205 while(get_string(key) && get_string(val))
206 params.push_back(make_pair(key, val));
207 go_to_next_item();
208 }
209 E(loc == cmd, F("Bad input to automate stdio: expected '%c' token") % cmd);
210 string item;
211 while (get_string(item))
212 {
213 cmdline.push_back(item);
214 }
215 return true;
216 }
217};
218
219struct automate_streambuf : public std::streambuf
220{
221private:
222 size_t _bufsize;
223 std::ostream *out;
224 automate_reader *in;
225 int cmdnum;
226 int err;
227public:
228 automate_streambuf(size_t bufsize)
229 : std::streambuf(), _bufsize(bufsize), out(0), in(0), cmdnum(0), err(0)
230 {
231 char *inbuf = new char[_bufsize];
232 setp(inbuf, inbuf + _bufsize);
233 }
234 automate_streambuf(std::ostream & o, size_t bufsize)
235 : std::streambuf(), _bufsize(bufsize), out(&o), in(0), cmdnum(0), err(0)
236 {
237 char *inbuf = new char[_bufsize];
238 setp(inbuf, inbuf + _bufsize);
239 }
240 automate_streambuf(automate_reader & i, size_t bufsize)
241 : std::streambuf(), _bufsize(bufsize), out(0), in(&i), cmdnum(0), err(0)
242 {
243 char *inbuf = new char[_bufsize];
244 setp(inbuf, inbuf + _bufsize);
245 }
246 ~automate_streambuf()
247 {}
248
249 void set_err(int e)
250 {
251 sync();
252 err = e;
253 }
254
255 void end_cmd()
256 {
257 _M_sync(true);
258 ++cmdnum;
259 err = 0;
260 }
261
262 virtual int sync()
263 {
264 _M_sync();
265 return 0;
266 }
267
268 void _M_sync(bool end = false)
269 {
270 if (!out)
271 {
272 setp(pbase(), pbase() + _bufsize);
273 return;
274 }
275 int num = pptr() - pbase();
276 if (num || end)
277 {
278 (*out) << cmdnum << ':'
279 << err << ':'
280 << (end?'l':'m') << ':'
281 << num << ':' << std::string(pbase(), num);
282 setp(pbase(), pbase() + _bufsize);
283 out->flush();
284 }
285 }
286 int_type
287 overflow(int_type c = traits_type::eof())
288 {
289 sync();
290 sputc(c);
291 return 0;
292 }
293};
294
295struct automate_ostream : public std::ostream
296{
297 automate_streambuf _M_autobuf;
298
299 automate_ostream(std::ostream &out, size_t blocksize)
300 : std::ostream(NULL),
301 _M_autobuf(out, blocksize)
302 { this->init(&_M_autobuf); }
303
304 ~automate_ostream()
305 {}
306
307 automate_streambuf *
308 rdbuf() const
309 { return const_cast<automate_streambuf *>(&_M_autobuf); }
310
311 void set_err(int e)
312 { _M_autobuf.set_err(e); }
313
314 void end_cmd()
315 { _M_autobuf.end_cmd(); }
316};
317
318
319CMD_AUTOMATE(stdio, "",
320 N_("Automates several commands in one run"),
321 "",
322 options::opts::automate_stdio_size)
323{
324 N(args.size() == 0,
325 F("no arguments needed"));
326
327 // initialize the database early so any calling process is notified
328 // immediately if a version discrepancy exists
329 app.db.ensure_open();
330
331 automate_ostream os(output, app.opts.automate_stdio_size);
332 automate_reader ar(std::cin);
333 vector<pair<string, string> > params;
334 vector<string> cmdline;
335 while(ar.get_command(params, cmdline))//while(!EOF)
336 {
337 args_vector args;
338 vector<string>::iterator i = cmdline.begin();
339 E(i != cmdline.end(),
340 F("Bad input to automate stdio: command name is missing"));
341 for (; i != cmdline.end(); ++i)
342 {
343 args.push_back(arg_type(*i));
344 }
345 try
346 {
347 options::options_type opts;
348 opts = options::opts::all_options() - options::opts::globals();
349 opts.instantiate(&app.opts).reset();
350
351 command_id id;
352 for (args_vector::const_iterator iter = args.begin();
353 iter != args.end(); iter++)
354 id.push_back(utf8((*iter)()));
355
356 if (!id.empty())
357 {
358 I(!args.empty());
359
360 set< command_id > matches =
361 CMD_REF(automate)->complete_command(id);
362
363 if (matches.size() == 0)
364 {
365 N(false, F("no completions for this command"));
366 }
367 else if (matches.size() > 1)
368 {
369 N(false, F("multiple completions possible for this command"));
370 }
371
372 id = *matches.begin();
373
374 I(args.size() >= id.size());
375 for (command_id::size_type i = 0; i < id.size(); i++)
376 args.erase(args.begin());
377
378 command const * cmd = CMD_REF(automate)->find_command(id);
379 I(cmd != NULL);
380 automate const * acmd = reinterpret_cast< automate const * >(cmd);
381
382 opts = options::opts::globals() | acmd->opts();
383 opts.instantiate(&app.opts).from_key_value_pairs(params);
384 acmd->exec_from_automate(args, id, app, os);
385 }
386 else
387 opts.instantiate(&app.opts).from_key_value_pairs(params);
388 }
389 catch(informative_failure & f)
390 {
391 os.set_err(2);
392 //Do this instead of printing f.what directly so the output
393 //will be split into properly-sized blocks automatically.
394 os<<f.what();
395 }
396 os.end_cmd();
397 }
398}
399
400// Local Variables:
401// mode: C++
402// fill-column: 76
403// c-file-style: "gnu"
404// indent-tabs-mode: nil
405// End:
406// 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