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