monotone

monotone Mtn Source Tree

Root/monotone.cc

1// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*-
2// copyright (C) 2002, 2003 graydon hoare <graydon@pobox.com>
3// all rights reserved.
4// licensed to the public under the terms of the GNU GPL (>= 2)
5// see the file COPYING for details
6
7#include <config.h>
8
9#include <popt.h>
10#include <cstdio>
11#include <iterator>
12#include <iostream>
13#include <fstream>
14#include <sstream>
15
16#include <stdlib.h>
17#ifdef WIN32
18#include <libintl.h>
19#endif
20
21#include "app_state.hh"
22#include "commands.hh"
23#include "sanity.hh"
24#include "cleanup.hh"
25#include "file_io.hh"
26#include "transforms.hh"
27#include "ui.hh"
28#include "mt_version.hh"
29
30#define OPT_DEBUG 1
31#define OPT_HELP 2
32#define OPT_NOSTD 3
33#define OPT_NORC 4
34#define OPT_RCFILE 5
35#define OPT_DB_NAME 6
36#define OPT_KEY_NAME 7
37#define OPT_BRANCH_NAME 8
38#define OPT_QUIET 9
39#define OPT_VERSION 10
40#define OPT_DUMP 11
41#define OPT_TICKER 12
42#define OPT_FULL_VERSION 13
43#define OPT_REVISION 14
44#define OPT_MESSAGE 15
45#define OPT_ROOT 16
46#define OPT_DEPTH 17
47#define OPT_ARGFILE 18
48
49// main option processing and exception handling code
50
51using namespace std;
52
53char * argstr = NULL;
54long arglong = 0;
55
56struct poptOption options[] =
57 {
58 {"debug", 0, POPT_ARG_NONE, NULL, OPT_DEBUG, "print debug log to stderr while running", NULL},
59 {"dump", 0, POPT_ARG_STRING, &argstr, OPT_DUMP, "file to dump debugging log to, on failure", NULL},
60 {"quiet", 0, POPT_ARG_NONE, NULL, OPT_QUIET, "suppress log and progress messages", NULL},
61 {"help", 0, POPT_ARG_NONE, NULL, OPT_HELP, "display help message", NULL},
62 {"nostd", 0, POPT_ARG_NONE, NULL, OPT_NOSTD, "do not load standard lua hooks", NULL},
63 {"norc", 0, POPT_ARG_NONE, NULL, OPT_NORC, "do not load ~/.monotonerc or MT/monotonerc lua files", NULL},
64 {"rcfile", 0, POPT_ARG_STRING, &argstr, OPT_RCFILE, "load extra rc file", NULL},
65 {"key", 'k', POPT_ARG_STRING, &argstr, OPT_KEY_NAME, "set key for signatures", NULL},
66 {"db", 'd', POPT_ARG_STRING, &argstr, OPT_DB_NAME, "set name of database", NULL},
67 {"branch", 'b', POPT_ARG_STRING, &argstr, OPT_BRANCH_NAME, "select branch cert for operation", NULL},
68 {"version", 0, POPT_ARG_NONE, NULL, OPT_VERSION, "print version number, then exit", NULL},
69 {"full-version", 0, POPT_ARG_NONE, NULL, OPT_FULL_VERSION, "print detailed version number, then exit", NULL},
70 {"ticker", 0, POPT_ARG_STRING, &argstr, OPT_TICKER, "set ticker style (count|dot|none) [count]", NULL},
71 {"revision", 'r', POPT_ARG_STRING, &argstr, OPT_REVISION, "select revision id for operation", NULL},
72 {"message", 'm', POPT_ARG_STRING, &argstr, OPT_MESSAGE, "set commit changelog message", NULL},
73 {"root", 0, POPT_ARG_STRING, &argstr, OPT_ROOT, "limit search for working copy to specified root", NULL},
74 {"depth", 0, POPT_ARG_LONG, &arglong, OPT_DEPTH, "limit the log output to the given number of entries", NULL},
75 {"xargs", '@', POPT_ARG_STRING, &argstr, OPT_ARGFILE, "insert command line arguments taken from the given file", NULL},
76 { NULL, 0, 0, NULL, 0 }
77 };
78
79// there are 3 variables which serve as roots for our system.
80//
81// "global_sanity" is a global object, which contains the error logging
82// system, which is constructed once and used by any nana logging actions.
83// see cleanup.hh for it
84//
85// "cmds" is a static table in commands.cc which associates top-level
86// commands, given on the command-line, to various version control tasks.
87//
88// "app_state" is a non-static object type which contains all the
89// application state (filesystem, database, network, lua interpreter,
90// etc). you can make more than one of these, and feed them to a command in
91// the command table.
92
93// our main function is run inside a boost execution monitor. this monitor
94// portably sets up handlers for various fatal conditions (signals, win32
95// structured exceptions, etc) and provides a uniform reporting interface
96// to any exceptions it catches. we augment this with a helper atexit()
97// which will also dump our internal logs when an explicit clean shutdown
98// flag is not set.
99//
100// in other words, this program should *never* unexpectedly terminate
101// without dumping some diagnostics.
102
103static bool clean_shutdown;
104
105void
106dumper()
107{
108 if (!clean_shutdown)
109 global_sanity.dump_buffer();
110}
111
112
113struct
114utf8_argv
115{
116 int argc;
117 char **argv;
118
119 explicit utf8_argv(int ac, char **av)
120 : argc(ac),
121 argv(static_cast<char **>(malloc(ac * sizeof(char *))))
122 {
123 I(argv != NULL);
124 for (int i = 0; i < argc; ++i)
125 {
126 external ext(av[i]);
127 utf8 utf;
128 system_to_utf8(ext, utf);
129 argv[i] = static_cast<char *>(malloc(utf().size() + 1));
130 I(argv[i] != NULL);
131 memcpy(argv[i], utf().data(), utf().size());
132 argv[i][utf().size()] = static_cast<char>(0);
133 }
134 }
135
136 ~utf8_argv()
137 {
138 if (argv != NULL)
139 {
140 for (int i = 0; i < argc; ++i)
141 if (argv[i] != NULL)
142 free(argv[i]);
143 free(argv);
144 }
145 }
146};
147
148// Stupid type system tricks: to use a cleanup_ptr, we need to know the return
149// type of the cleanup function. But popt silently changed the return type of
150// poptFreeContext at some point, I guess because they thought it would be
151// "backwards compatible". We don't actually _use_ the return value of
152// poptFreeContext, so this little wrapper works.
153static void
154my_poptFreeContext(poptContext con)
155{
156 poptFreeContext(con);
157}
158
159// Read arguments from a file. The special file '-' means stdin.
160// Returned value must be free()'d, after arg parsing has completed.
161static const char **
162my_poptStuffArgFile(poptContext con, utf8 const & filename)
163{
164 // popt currently (Debian version 1.7-5) has a bug that's triggered
165 // when poptStuffArgs() is called more than once. The symptom is that
166 // while the options from the second run are processed as such, they
167 // are ALSO passed back to the application as non-options...
168 // Our current workaround is simply to forbid -@ to be used more than
169 // once. -- Richard Levitte
170 static bool used_at_sign = false;
171 N(!used_at_sign, F("-@ can currently only be used once.\n"));
172 used_at_sign = true;
173
174 utf8 argstr;
175 {
176 data dat;
177 read_data_for_command_line(filename, dat);
178 external ext(dat());
179 system_to_utf8(ext, argstr);
180 }
181
182 const char **argv = 0;
183 int argc = 0;
184 int rc;
185
186 // Parse the string. It's OK if there are no arguments.
187 rc = poptParseArgvString(argstr().c_str(), &argc, &argv);
188 N(rc >= 0 || rc == POPT_ERROR_NOARG,
189 F("problem parsing arguments from file %s: %s")
190 % filename % poptStrerror(rc));
191
192 if (rc != POPT_ERROR_NOARG)
193 {
194 // poptStuffArgs does not take an argc argument, but rather requires that
195 // the argv array be null-terminated.
196 I(argv[argc] == NULL);
197 N((rc = poptStuffArgs(con, argv)) >= 0,
198F("weird error when stuffing arguments read from %s: %s\n")
199% filename % poptStrerror(rc));
200 }
201 else
202 {
203 free(argv); // just in case there was something...
204 argv = 0;
205 }
206
207 return argv;
208}
209
210int
211cpp_main(int argc, char ** argv)
212{
213
214 clean_shutdown = false;
215
216 atexit(&dumper);
217
218 // go-go gadget i18n
219
220 setlocale(LC_CTYPE, "");
221 setlocale(LC_MESSAGES, "");
222 bindtextdomain(PACKAGE, LOCALEDIR);
223 textdomain(PACKAGE);
224
225 {
226 std::ostringstream cmdline_ss;
227 for (int i = 0; i < argc; ++i)
228 {
229 if (i)
230 cmdline_ss << ", ";
231 cmdline_ss << "'" << argv[i] << "'";
232 }
233 L(F("command line: %s\n") % cmdline_ss.str());
234 }
235
236 L(F("set locale: LC_CTYPE=%s, LC_MESSAGES=%s\n")
237 % (setlocale(LC_CTYPE, NULL) == NULL ? "n/a" : setlocale(LC_CTYPE, NULL))
238 % (setlocale(LC_MESSAGES, NULL) == NULL ? "n/a" : setlocale(LC_CTYPE, NULL)));
239
240 // decode all argv values into a UTF-8 array
241
242 save_initial_path();
243 utf8_argv uv(argc, argv);
244
245 // prepare for arg parsing
246
247 cleanup_ptr<poptContext, void>
248 ctx(poptGetContext(NULL, argc, (char const **) uv.argv, options, 0),
249 &my_poptFreeContext);
250
251 // process main program options
252
253 int ret = 0;
254 int opt;
255 bool requested_help = false;
256
257 // keep a list of argv vectors created by get_args_from_file, since they
258 // must be individually free()'d, but not until arg parsing is done.
259 std::vector<const char**> sub_argvs;
260
261 poptSetOtherOptionHelp(ctx(), "[OPTION...] command [ARGS...]\n");
262
263 try
264 {
265
266 app_state app;
267
268 while ((opt = poptGetNextOpt(ctx())) > 0)
269 {
270 switch(opt)
271 {
272 case OPT_DEBUG:
273 global_sanity.set_debug();
274 break;
275
276 case OPT_QUIET:
277 global_sanity.set_quiet();
278 break;
279
280 case OPT_NOSTD:
281 app.set_stdhooks(false);
282 break;
283
284 case OPT_NORC:
285 app.set_rcfiles(false);
286 break;
287
288 case OPT_RCFILE:
289 app.add_rcfile(absolutify(tilde_expand(string(argstr))));
290 break;
291
292 case OPT_DUMP:
293 global_sanity.filename = absolutify(tilde_expand(string(argstr)));
294 break;
295
296 case OPT_DB_NAME:
297 app.set_database(absolutify(tilde_expand(string(argstr))));
298 break;
299
300 case OPT_TICKER:
301 if (string(argstr) == "dot")
302 ui.set_tick_writer(new tick_write_dot);
303 else if (string(argstr) == "count")
304 ui.set_tick_writer(new tick_write_count);
305 else if (string(argstr) == "none")
306 ui.set_tick_writer(new tick_write_nothing);
307 else
308 requested_help = true;
309 break;
310
311 case OPT_KEY_NAME:
312 app.set_signing_key(string(argstr));
313 break;
314
315 case OPT_BRANCH_NAME:
316 app.set_branch(string(argstr));
317 break;
318
319 case OPT_VERSION:
320 print_version();
321 clean_shutdown = true;
322 return 0;
323
324 case OPT_FULL_VERSION:
325 print_full_version();
326 clean_shutdown = true;
327 return 0;
328
329 case OPT_REVISION:
330 app.add_revision(string(argstr));
331 break;
332
333 case OPT_MESSAGE:
334 app.set_message(string(argstr));
335 break;
336
337 case OPT_ROOT:
338 app.set_root(string(argstr));
339 break;
340
341 case OPT_DEPTH:
342 app.set_depth(arglong);
343 break;
344
345 case OPT_ARGFILE:
346 sub_argvs.push_back(my_poptStuffArgFile(ctx(),
347 utf8(string(argstr))));
348 break;
349
350 case OPT_HELP:
351 default:
352 requested_help = true;
353 break;
354 }
355 }
356
357 // verify that there are no errors in the command line
358
359 N(opt == -1,
360 F("syntax error near the \"%s\" option: %s") %
361 poptBadOption(ctx(), POPT_BADOPTION_NOALIAS) % poptStrerror(opt));
362
363 // stop here if they asked for help
364
365 if (requested_help)
366 {
367 if (poptPeekArg(ctx()))
368 {
369 string cmd(poptGetArg(ctx()));
370 throw usage(cmd);
371 }
372 else
373 throw usage("");
374 }
375
376 // at this point we allow a working copy (meaning search for it
377 // and if found read MT/options) but don't require it. certain
378 // commands may subsequently require a working copy or fail
379
380 app.allow_working_copy();
381
382 // main options processed, now invoke the
383 // sub-command w/ remaining args
384
385 if (!poptPeekArg(ctx()))
386 {
387 throw usage("");
388 }
389 else
390 {
391 string cmd(poptGetArg(ctx()));
392 vector<utf8> args;
393 while(poptPeekArg(ctx()))
394 {
395 args.push_back(utf8(string(poptGetArg(ctx()))));
396 }
397 // we've copied everything we want from the command line into our
398 // own data structures, so we can finally delete popt's malloc'ed
399 // argv stuff.
400 for (std::vector<const char**>::const_iterator i = sub_argvs.begin();
401 i != sub_argvs.end(); ++i)
402 free(*i);
403 ret = commands::process(app, cmd, args);
404 }
405 }
406 catch (usage & u)
407 {
408 poptPrintHelp(ctx(), stdout, 0);
409 cout << endl;
410 commands::explain_usage(u.which, cout);
411 clean_shutdown = true;
412 return 0;
413 }
414 catch (informative_failure & inf)
415 {
416 ui.inform(inf.what);
417 clean_shutdown = true;
418 return 1;
419 }
420
421 clean_shutdown = true;
422 return ret;
423}

Archive Download this file

Branches

Tags

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