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/popt.h"
10#include <cstdio>
11#include <strings.h>
12#include <iterator>
13#include <iostream>
14#include <fstream>
15#include <sstream>
16
17#include <stdlib.h>
18#ifdef WIN32
19#include <libintl.h>
20#endif
21
22#include "botan/botan.h"
23
24#include "app_state.hh"
25#include "commands.hh"
26#include "sanity.hh"
27#include "cleanup.hh"
28#include "file_io.hh"
29#include "transforms.hh"
30#include "ui.hh"
31#include "mt_version.hh"
32#include "options.hh"
33#include "paths.hh"
34
35// main option processing and exception handling code
36
37using namespace std;
38
39char * argstr = NULL;
40long arglong = 0;
41
42// Options are split between two tables. The first one is command-specific
43// options (hence the `c' in `coptions'). The second is the global one
44// with options that aren't tied to specific commands.
45//
46// the intent is to ensure that any command specific options mean the same
47// thing to all commands that use them
48
49struct poptOption coptions[] =
50 {
51 {"branch", 'b', POPT_ARG_STRING, &argstr, OPT_BRANCH_NAME, gettext_noop("select branch cert for operation"), NULL},
52 {"revision", 'r', POPT_ARG_STRING, &argstr, OPT_REVISION, gettext_noop("select revision id for operation"), NULL},
53 {"message", 'm', POPT_ARG_STRING, &argstr, OPT_MESSAGE, gettext_noop("set commit changelog message"), NULL},
54 {"message-file", 0, POPT_ARG_STRING, &argstr, OPT_MSGFILE, gettext_noop("set filename containing commit changelog message"), NULL},
55 {"date", 0, POPT_ARG_STRING, &argstr, OPT_DATE, gettext_noop("override date/time for commit"), NULL},
56 {"author", 0, POPT_ARG_STRING, &argstr, OPT_AUTHOR, gettext_noop("override author for commit"), NULL},
57 {"depth", 0, POPT_ARG_LONG, &arglong, OPT_DEPTH, gettext_noop("limit the number of levels of directories to descend"), NULL},
58 {"last", 0, POPT_ARG_LONG, &arglong, OPT_LAST, gettext_noop("limit the log output to the given number of entries"), NULL},
59 {"pid-file", 0, POPT_ARG_STRING, &argstr, OPT_PIDFILE, gettext_noop("record process id of server"), NULL},
60 {"brief", 0, POPT_ARG_NONE, NULL, OPT_BRIEF, gettext_noop("print a brief version of the normal output"), NULL},
61 {"diffs", 0, POPT_ARG_NONE, NULL, OPT_DIFFS, gettext_noop("print diffs along with logs"), NULL},
62 {"no-merges", 0, POPT_ARG_NONE, NULL, OPT_NO_MERGES, gettext_noop("skip merges when printing logs"), NULL},
63 {"set-default", 0, POPT_ARG_NONE, NULL, OPT_SET_DEFAULT, gettext_noop("use the current arguments as the future default"), NULL},
64 {"exclude", 0, POPT_ARG_STRING, &argstr, OPT_EXCLUDE, gettext_noop("leave out anything described by its argument"), NULL},
65 {"unified", 0, POPT_ARG_NONE, NULL, OPT_UNIFIED_DIFF, gettext_noop("use unified diff format"), NULL},
66 {"context", 0, POPT_ARG_NONE, NULL, OPT_CONTEXT_DIFF, gettext_noop("use context diff format"), NULL},
67 {"external", 0, POPT_ARG_NONE, NULL, OPT_EXTERNAL_DIFF, gettext_noop("use external diff hook for generating diffs"), NULL},
68 {"diff-args", 0, POPT_ARG_STRING, &argstr, OPT_EXTERNAL_DIFF_ARGS, gettext_noop("argument to pass external diff hook"), NULL},
69 {"lca", 0, POPT_ARG_NONE, NULL, OPT_LCA, gettext_noop("use least common ancestor as ancestor for merge"), NULL},
70 {"execute", 'e', POPT_ARG_NONE, NULL, OPT_EXECUTE, gettext_noop("perform the associated file operation"), NULL},
71 { NULL, 0, 0, NULL, 0, NULL, NULL }
72 };
73
74struct poptOption options[] =
75 {
76 // Use the coptions table as well.
77 { NULL, 0, POPT_ARG_INCLUDE_TABLE, coptions, 0, NULL, NULL },
78
79 {"debug", 0, POPT_ARG_NONE, NULL, OPT_DEBUG, gettext_noop("print debug log to stderr while running"), NULL},
80 {"dump", 0, POPT_ARG_STRING, &argstr, OPT_DUMP, gettext_noop("file to dump debugging log to, on failure"), NULL},
81 {"quiet", 0, POPT_ARG_NONE, NULL, OPT_QUIET, gettext_noop("suppress log and progress messages"), NULL},
82 {"help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, gettext_noop("display help message"), NULL},
83 {"version", 0, POPT_ARG_NONE, NULL, OPT_VERSION, gettext_noop("print version number, then exit"), NULL},
84 {"full-version", 0, POPT_ARG_NONE, NULL, OPT_FULL_VERSION, gettext_noop("print detailed version number, then exit"), NULL},
85 {"xargs", '@', POPT_ARG_STRING, &argstr, OPT_ARGFILE, gettext_noop("insert command line arguments taken from the given file"), NULL},
86 {"ticker", 0, POPT_ARG_STRING, &argstr, OPT_TICKER, gettext_noop("set ticker style (count|dot|none)"), NULL},
87 {"nostd", 0, POPT_ARG_NONE, NULL, OPT_NOSTD, gettext_noop("do not load standard lua hooks"), NULL},
88 {"norc", 0, POPT_ARG_NONE, NULL, OPT_NORC, gettext_noop("do not load ~/.monotone/monotonerc or MT/monotonerc lua files"), NULL},
89 {"rcfile", 0, POPT_ARG_STRING, &argstr, OPT_RCFILE, gettext_noop("load extra rc file"), NULL},
90 {"key", 'k', POPT_ARG_STRING, &argstr, OPT_KEY_NAME, gettext_noop("set key for signatures"), NULL},
91 {"db", 'd', POPT_ARG_STRING, &argstr, OPT_DB_NAME, gettext_noop("set name of database"), NULL},
92 {"root", 0, POPT_ARG_STRING, &argstr, OPT_ROOT, gettext_noop("limit search for working copy to specified root"), NULL},
93 {"verbose", 0, POPT_ARG_NONE, NULL, OPT_VERBOSE, gettext_noop("verbose completion output"), NULL},
94 { NULL, 0, 0, NULL, 0, NULL, NULL }
95 };
96
97// there are 3 variables which serve as roots for our system.
98//
99// "global_sanity" is a global object, which contains the error logging
100// system, which is constructed once and used by any nana logging actions.
101// see cleanup.hh for it
102//
103// "cmds" is a static table in commands.cc which associates top-level
104// commands, given on the command-line, to various version control tasks.
105//
106// "app_state" is a non-static object type which contains all the
107// application state (filesystem, database, network, lua interpreter,
108// etc). you can make more than one of these, and feed them to a command in
109// the command table.
110
111// our main function is run inside a boost execution monitor. this monitor
112// portably sets up handlers for various fatal conditions (signals, win32
113// structured exceptions, etc) and provides a uniform reporting interface
114// to any exceptions it catches. we augment this with a helper atexit()
115// which will also dump our internal logs when an explicit clean shutdown
116// flag is not set.
117//
118// in other words, this program should *never* unexpectedly terminate
119// without dumping some diagnostics.
120
121static bool clean_shutdown;
122
123void
124dumper()
125{
126 if (!clean_shutdown)
127 global_sanity.dump_buffer();
128
129 Botan::Init::deinitialize();
130}
131
132
133struct
134utf8_argv
135{
136 int argc;
137 char **argv;
138
139 explicit utf8_argv(int ac, char **av)
140 : argc(ac),
141 argv(static_cast<char **>(malloc(ac * sizeof(char *))))
142 {
143 I(argv != NULL);
144 for (int i = 0; i < argc; ++i)
145 {
146 external ext(av[i]);
147 utf8 utf;
148 system_to_utf8(ext, utf);
149 argv[i] = static_cast<char *>(malloc(utf().size() + 1));
150 I(argv[i] != NULL);
151 memcpy(argv[i], utf().data(), utf().size());
152 argv[i][utf().size()] = static_cast<char>(0);
153 }
154 }
155
156 ~utf8_argv()
157 {
158 if (argv != NULL)
159 {
160 for (int i = 0; i < argc; ++i)
161 if (argv[i] != NULL)
162 free(argv[i]);
163 free(argv);
164 }
165 }
166};
167
168// Stupid type system tricks: to use a cleanup_ptr, we need to know the return
169// type of the cleanup function. But popt silently changed the return type of
170// poptFreeContext at some point, I guess because they thought it would be
171// "backwards compatible". We don't actually _use_ the return value of
172// poptFreeContext, so this little wrapper works.
173static void
174my_poptFreeContext(poptContext con)
175{
176 poptFreeContext(con);
177}
178
179// Read arguments from a file. The special file '-' means stdin.
180// Returned value must be free()'d, after arg parsing has completed.
181static void
182my_poptStuffArgFile(poptContext con, utf8 const & filename)
183{
184 utf8 argstr;
185 {
186 data dat;
187 read_data_for_command_line(filename, dat);
188 external ext(dat());
189 system_to_utf8(ext, argstr);
190 }
191
192 const char **argv = 0;
193 int argc = 0;
194 int rc;
195
196 // Parse the string. It's OK if there are no arguments.
197 rc = poptParseArgvString(argstr().c_str(), &argc, &argv);
198 N(rc >= 0 || rc == POPT_ERROR_NOARG,
199 F("problem parsing arguments from file %s: %s")
200 % filename % poptStrerror(rc));
201
202 if (rc != POPT_ERROR_NOARG)
203 {
204 // poptStuffArgs does not take an argc argument, but rather requires that
205 // the argv array be null-terminated.
206 I(argv[argc] == NULL);
207 N((rc = poptStuffArgs(con, argv)) >= 0,
208 F("weird error when stuffing arguments read from %s: %s\n")
209 % filename % poptStrerror(rc));
210 }
211
212 free(argv);
213}
214
215static string
216coption_string(int o)
217{
218 char buf[2] = { 0,0 };
219 for(struct poptOption *opt = coptions; opt->val; opt++)
220 if (o == opt->val)
221 {
222 buf[0] = opt->shortName;
223 return opt->longName
224 ? string("--") + string(opt->longName)
225 : string("-") + string(buf);
226 }
227 return string();
228}
229
230int
231cpp_main(int argc, char ** argv)
232{
233 clean_shutdown = false;
234 int ret = 0;
235
236 atexit(&dumper);
237
238 // go-go gadget i18n
239
240 setlocale(LC_ALL, "");
241 bindtextdomain(PACKAGE, LOCALEDIR);
242 textdomain(PACKAGE);
243
244
245 // we want to catch any early informative_failures due to charset
246 // conversion etc
247 try
248 {
249
250 {
251 std::ostringstream cmdline_ss;
252 for (int i = 0; i < argc; ++i)
253 {
254 if (i)
255 cmdline_ss << ", ";
256 cmdline_ss << "'" << argv[i] << "'";
257 }
258 L(F("command line: %s\n") % cmdline_ss.str());
259 }
260
261 L(F("set locale: LC_ALL=%s\n")
262 % (setlocale(LC_ALL, NULL) == NULL ? "n/a" : setlocale(LC_ALL, NULL)));
263
264 // Set up secure memory allocation etc
265 Botan::Init::initialize();
266 Botan::set_default_allocator("malloc");
267
268 // decode all argv values into a UTF-8 array
269
270 save_initial_path();
271 utf8_argv uv(argc, argv);
272
273 // prepare for arg parsing
274
275 cleanup_ptr<poptContext, void>
276 ctx(poptGetContext(NULL, argc, (char const **) uv.argv, options, 0),
277 &my_poptFreeContext);
278
279 set<int> local_options;
280 for (poptOption *opt = coptions; opt->val; opt++)
281 local_options.insert(opt->val);
282
283 // process main program options
284
285 int opt;
286 bool requested_help = false;
287 set<int> used_local_options;
288
289 poptSetOtherOptionHelp(ctx(), _("[OPTION...] command [ARGS...]\n"));
290
291 try
292 {
293 app_state app;
294
295 while ((opt = poptGetNextOpt(ctx())) > 0)
296 {
297 if (local_options.find(opt) != local_options.end())
298 used_local_options.insert(opt);
299
300 switch(opt)
301 {
302 case OPT_DEBUG:
303 global_sanity.set_debug();
304 break;
305
306 case OPT_QUIET:
307 global_sanity.set_quiet();
308 break;
309
310 case OPT_NOSTD:
311 app.set_stdhooks(false);
312 break;
313
314 case OPT_NORC:
315 app.set_rcfiles(false);
316 break;
317
318 case OPT_VERBOSE:
319 app.set_verbose(true);
320 break;
321
322 case OPT_RCFILE:
323 app.add_rcfile(string(argstr));
324 break;
325
326 case OPT_DUMP:
327 global_sanity.filename = system_path(argstr);
328 break;
329
330 case OPT_DB_NAME:
331 app.set_database(system_path(argstr));
332 break;
333
334 case OPT_TICKER:
335 if (string(argstr) == "dot")
336 ui.set_tick_writer(new tick_write_dot);
337 else if (string(argstr) == "count")
338 ui.set_tick_writer(new tick_write_count);
339 else if (string(argstr) == "none")
340 ui.set_tick_writer(new tick_write_nothing);
341 else
342 requested_help = true;
343 break;
344
345 case OPT_KEY_NAME:
346 app.set_signing_key(string(argstr));
347 break;
348
349 case OPT_BRANCH_NAME:
350 app.set_branch(string(argstr));
351 break;
352
353 case OPT_VERSION:
354 print_version();
355 clean_shutdown = true;
356 return 0;
357
358 case OPT_FULL_VERSION:
359 print_full_version();
360 clean_shutdown = true;
361 return 0;
362
363 case OPT_REVISION:
364 app.add_revision(string(argstr));
365 break;
366
367 case OPT_MESSAGE:
368 app.set_message(string(argstr));
369 break;
370
371 case OPT_MSGFILE:
372 app.set_message_file(string(argstr));
373 break;
374
375 case OPT_DATE:
376 app.set_date(string(argstr));
377 break;
378
379 case OPT_AUTHOR:
380 app.set_author(string(argstr));
381 break;
382
383 case OPT_ROOT:
384 app.set_root(system_path(argstr));
385 break;
386
387 case OPT_LAST:
388 app.set_last(arglong);
389 break;
390
391 case OPT_DEPTH:
392 app.set_depth(arglong);
393 break;
394
395 case OPT_BRIEF:
396 global_sanity.set_brief();
397 break;
398
399 case OPT_DIFFS:
400 app.diffs = true;
401 break;
402
403 case OPT_NO_MERGES:
404 app.no_merges = true;
405 break;
406
407 case OPT_SET_DEFAULT:
408 app.set_default = true;
409 break;
410
411 case OPT_EXCLUDE:
412 app.add_exclude(utf8(string(argstr)));
413 break;
414
415 case OPT_PIDFILE:
416 app.set_pidfile(system_path(argstr));
417 break;
418
419 case OPT_ARGFILE:
420 my_poptStuffArgFile(ctx(), utf8(string(argstr)));
421 break;
422
423 case OPT_UNIFIED_DIFF:
424 app.set_diff_format(unified_diff);
425 break;
426
427 case OPT_CONTEXT_DIFF:
428 app.set_diff_format(context_diff);
429 break;
430
431 case OPT_EXTERNAL_DIFF:
432 app.set_diff_format(external_diff);
433 break;
434
435 case OPT_EXTERNAL_DIFF_ARGS:
436 app.set_diff_args(utf8(string(argstr)));
437 break;
438
439 case OPT_LCA:
440 app.use_lca = true;
441 break;
442
443 case OPT_EXECUTE:
444 app.execute = true;
445 break;
446
447 case OPT_HELP:
448 default:
449 requested_help = true;
450 break;
451 }
452 }
453
454 // verify that there are no errors in the command line
455
456 N(opt == -1,
457 F("syntax error near the \"%s\" option: %s") %
458 poptBadOption(ctx(), POPT_BADOPTION_NOALIAS) % poptStrerror(opt));
459
460 // complete the command if necessary
461
462 string cmd;
463 if (poptPeekArg(ctx()))
464 {
465 cmd = commands::complete_command(poptGetArg(ctx()));
466 }
467
468 // stop here if they asked for help
469
470 if (requested_help)
471 {
472 throw usage(cmd); // cmd may be empty, and that's fine.
473 }
474
475 // at this point we allow a working copy (meaning search for it
476 // and if found read MT/options) but don't require it. certain
477 // commands may subsequently require a working copy or fail
478
479 app.allow_working_copy();
480
481 // main options processed, now invoke the
482 // sub-command w/ remaining args
483
484 if (cmd.empty())
485 {
486 throw usage("");
487 }
488 else
489 {
490 // Make sure the local options used are really used by the
491 // given command.
492 set<int> command_options = commands::command_options(cmd);
493 for (set<int>::const_iterator i = used_local_options.begin();
494 i != used_local_options.end(); ++i)
495 N(command_options.find(*i) != command_options.end(),
496 F("monotone %s doesn't use the option %s")
497 % cmd % coption_string(*i));
498
499 vector<utf8> args;
500 while(poptPeekArg(ctx()))
501 {
502 args.push_back(utf8(string(poptGetArg(ctx()))));
503 }
504 ret = commands::process(app, cmd, args);
505 }
506 }
507 catch (usage & u)
508 {
509 // Make sure to hide documentation that's not part of
510 // the current command.
511 set<int> command_options = commands::command_options(u.which);
512 int count = 0;
513 for (poptOption *o = coptions; o->val != 0; o++)
514 {
515 if (command_options.find(o->val) != command_options.end())
516 {
517 o->argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
518 L(F("Removed 'hidden' from option # %d\n") % o->argInfo);
519 count++;
520 }
521 else
522 {
523 o->argInfo |= POPT_ARGFLAG_DOC_HIDDEN;
524 L(F("Added 'hidden' to option # %d\n") % o->argInfo);
525 }
526 }
527 free((void *)options[0].descrip); options[0].descrip = NULL;
528 if (count != 0)
529 {
530 ostringstream sstr;
531 sstr << F("Options specific to 'monotone %s':") % u.which;
532 options[0].descrip = strdup(sstr.str().c_str());
533
534 options[0].argInfo |= POPT_ARGFLAG_DOC_HIDDEN;
535 L(F("Added 'hidden' to option # %d\n") % options[0].argInfo);
536 }
537
538 poptPrintHelp(ctx(), stdout, 0);
539 cout << endl;
540 commands::explain_usage(u.which, cout);
541 clean_shutdown = true;
542 return 0;
543 }
544 }
545 catch (informative_failure & inf)
546 {
547 ui.inform(inf.what);
548 clean_shutdown = true;
549 return 1;
550 }
551
552 clean_shutdown = true;
553 return ret;
554}

Archive Download this file

Branches

Tags

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