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 <iosfwd>
11#include <iostream>
12#include <map>
13
14#include <boost/function.hpp>
15#include <boost/bind.hpp>
16
17#include "cmd.hh"
18
19using std::map;
20using std::ostream;
21using std::string;
22using std::vector;
23
24namespace automation {
25 // When this is split into multiple files, there will not be any
26 // guarantees about initialization order. So, use something we can
27 // initialize ourselves.
28 static map<string, automate * const> * automations;
29 automate::automate(string const &n, string const &p)
30 : name(n), params(p)
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
43void
44automate_command(utf8 cmd, vector<utf8> args,
45 string const & root_cmd_name,
46 app_state & app,
47 ostream & output)
48{
49 map<string, automation::automate * const>::const_iterator
50 i = automation::automations->find(cmd());
51 if (i == automation::automations->end())
52 throw usage(root_cmd_name);
53 else
54 i->second->run(args, root_cmd_name, app, output);
55}
56
57static string const interface_version = "2.2";
58
59// Name: interface_version
60// Arguments: none
61// Added in: 0.0
62// Purpose: Prints version of automation interface. Major number increments
63// whenever a backwards incompatible change is made; minor number increments
64// whenever any change is made (but is reset when major number increments).
65// Output format: "<decimal number>.<decimal number>\n". Always matches
66// "[0-9]+\.[0-9]+\n".
67// Error conditions: None.
68AUTOMATE(interface_version, "")
69{
70 if (args.size() != 0)
71 throw usage(help_name);
72
73 output << interface_version << "\n";
74}
75
76void
77automate_command(utf8 cmd, std::vector<utf8> args,
78 std::string const & root_cmd_name,
79 app_state & app,
80 std::ostream & output);
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// We use our own stringbuf class so we can put in a callback on write.
120// This lets us dump output at a set length, rather than waiting until
121// we have all of the output.
122
123typedef std::basic_stringbuf<char,
124 std::char_traits<char>,
125 std::allocator<char> > char_stringbuf;
126struct my_stringbuf : public char_stringbuf
127{
128private:
129 std::streamsize written;
130 boost::function1<void, int> on_write;
131 std::streamsize last_call;
132 std::streamsize call_every;
133 bool clear;
134public:
135 my_stringbuf() : char_stringbuf(),
136 written(0),
137 last_call(0),
138 call_every(constants::automate_stdio_size)
139 {}
140 virtual std::streamsize
141 xsputn(const char_stringbuf::char_type* __s, std::streamsize __n)
142 {
143 std::streamsize ret=char_stringbuf::xsputn(__s, __n);
144 written+=__n;
145 while(written>=last_call+call_every)
146 {
147 if(on_write)
148 on_write(call_every);
149 last_call+=call_every;
150 }
151 return ret;
152 }
153 virtual int sync()
154 {
155 int ret=char_stringbuf::sync();
156 if(on_write)
157 on_write(-1);
158 last_call=written;
159 return ret;
160 }
161 void set_on_write(boost::function1<void, int> x)
162 {
163 on_write = x;
164 }
165};
166
167void print_some_output(int cmdnum,
168 int err,
169 bool last,
170 string const & text,
171 ostream & s,
172 int & pos,
173 int size)
174{
175 if(size==-1)
176 {
177 while(text.size()-pos > constants::automate_stdio_size)
178 {
179 s<<cmdnum<<':'<<err<<':'<<'m'<<':';
180 s<<constants::automate_stdio_size<<':'
181 <<text.substr(pos, constants::automate_stdio_size);
182 pos+=constants::automate_stdio_size;
183 s.flush();
184 }
185 s<<cmdnum<<':'<<err<<':'<<(last?'l':'m')<<':';
186 s<<(text.size()-pos)<<':'<<text.substr(pos);
187 pos=text.size();
188 }
189 else
190 {
191 I((unsigned int)(size) <= constants::automate_stdio_size);
192 s<<cmdnum<<':'<<err<<':'<<(last?'l':'m')<<':';
193 s<<size<<':'<<text.substr(pos, size);
194 pos+=size;
195 }
196 s.flush();
197}
198
199static ssize_t automate_stdio_read(int d, void *buf, size_t nbytes)
200{
201 ssize_t rv;
202
203 rv = read(d, buf, nbytes);
204
205 E(rv >= 0, F("read from client failed with error code: %d") % rv);
206 return rv;
207}
208
209AUTOMATE(stdio, "")
210{
211 if (args.size() != 0)
212 throw usage(help_name);
213 int cmdnum = 0;
214 char c;
215 ssize_t n=1;
216 while(n)//while(!EOF)
217 {
218 string x;
219 utf8 cmd;
220 args.clear();
221 bool first=true;
222 int toklen=0;
223 bool firstchar=true;
224 for(n=automate_stdio_read(0, &c, 1); c != 'l' && n; n=automate_stdio_read(0, &c, 1))
225 ;
226 for(n=automate_stdio_read(0, &c, 1); c!='e' && n; n=automate_stdio_read(0, &c, 1))
227 {
228 if(c<='9' && c>='0')
229 {
230 toklen=(toklen*10)+(c-'0');
231 }
232 else if(c == ':')
233 {
234 char *tok=new char[toklen];
235 int count=0;
236 while(count<toklen)
237 count+=automate_stdio_read(0, tok+count, toklen-count);
238 if(first)
239 cmd=utf8(string(tok, toklen));
240 else
241 args.push_back(utf8(string(tok, toklen)));
242 toklen=0;
243 delete[] tok;
244 first=false;
245 }
246 else
247 {
248 N(false, F("Bad input to automate stdio"));
249 }
250 firstchar=false;
251 }
252 if(cmd() != "")
253 {
254 int outpos=0;
255 int err;
256 std::ostringstream s;
257 my_stringbuf sb;
258 sb.set_on_write(boost::bind(print_some_output,
259 cmdnum,
260 boost::ref(err),
261 false,
262 boost::bind(&my_stringbuf::str, &sb),
263 boost::ref(output),
264 boost::ref(outpos),
265 _1));
266 {
267 // Do not use s.std::basic_ios<...>::rdbuf here,
268 // it confuses VC8.
269 using std::basic_ios;
270 s.basic_ios<char, std::char_traits<char> >::rdbuf(&sb);
271 }
272 try
273 {
274 err=0;
275 automate_command(cmd, args, help_name, app, s);
276 }
277 catch(usage &)
278 {
279 if(sb.str().size())
280 s.flush();
281 err=1;
282 commands::explain_usage(help_name, s);
283 }
284 catch(informative_failure & f)
285 {
286 if(sb.str().size())
287 s.flush();
288 err=2;
289 //Do this instead of printing f.what directly so the output
290 //will be split into properly-sized blocks automatically.
291 s<<f.what;
292 }
293 print_some_output(cmdnum, err, true, sb.str(),
294 output, outpos, -1);
295 }
296 cmdnum++;
297 }
298}
299
300
301CMD_PARAMS_FN(automate, N_("automation"),
302 N_("automation interface"),
303 OPT_NONE)
304{
305 if (args.size() == 0)
306 throw usage(name);
307
308 vector<utf8>::const_iterator i = args.begin();
309 utf8 cmd = *i;
310 ++i;
311 vector<utf8> cmd_args(i, args.end());
312
313 make_io_binary();
314
315 automate_command(cmd, cmd_args, name, app, std::cout);
316}
317
318std::string commands::cmd_automate::params()
319{
320 std::string out;
321 map<string, automation::automate * const>::const_iterator i;
322 for (i = automation::automations->begin();
323 i != automation::automations->end(); ++i)
324 {
325 char const * const p = commands::safe_gettext(i->second->params.c_str());
326 out += i->second->name + " " + p;
327 if (out[out.size()-1] != '\n')
328 out += "\n";
329 }
330 return out;
331}
332
333
334// Local Variables:
335// mode: C++
336// fill-column: 76
337// c-file-style: "gnu"
338// indent-tabs-mode: nil
339// End:
340// 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