Below is the file 'app_state.cc' from this revision. You can also download the file.

#include <iostream>
#include <string>
#include <vector>
#ifdef WIN32
#include <io.h> /* for chdir() */
#else
#include <unistd.h> /* for chdir() on POSIX */
#endif
#include <cstdlib>              // for strtoul()

#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/filesystem/exception.hpp>

#include "app_state.hh"
#include "database.hh"
#include "file_io.hh"
#include "sanity.hh"
#include "transforms.hh"
#include "work.hh"
#include "platform.hh"

// copyright (C) 2002, 2003 graydon hoare <graydon@pobox.com>
// all rights reserved.
// licensed to the public under the terms of the GNU GPL (>= 2)
// see the file COPYING for details

using namespace std;

static string const database_option("database");
static string const branch_option("branch");
static string const key_option("key");

app_state::app_state()
  : branch_name(""), db(system_path()), stdhooks(true), rcfiles(true), diffs(false),
    no_merges(false), set_default(false), verbose(false), search_root("/"),
    depth(-1), last(-1), diff_format(unified_diff), diff_args_provided(false),
    use_lca(false), execute(false)
{
  db.set_app(this);
}

app_state::~app_state()
{
}

void
app_state::allow_working_copy()
{
  L(F("initializing from directory %s\n") % fs::initial_path().string());
  found_working_copy = find_and_go_to_working_copy(search_root);

  if (found_working_copy)
    {
      read_options();

      system_path dbname = system_path(options[database_option]);
      if (!dbname.empty()) db.set_filename(dbname);
      if (branch_name().empty())
        branch_name = options[branch_option];
      L(F("branch name is '%s'\n") % branch_name());
      internalize_rsa_keypair_id(options[key_option], signing_key);

      if (global_sanity.filename.empty())
        {
          bookkeeping_path dump_path;
          get_local_dump_path(dump_path);
          L(F("setting dump path to %s\n") % dump_path);
          // the 'true' means that, e.g., if we're running checkout, then it's
          // okay for dumps to go into our starting working dir's MT rather
          // than the checked-out dir's MT.
          global_sanity.filename = system_path(dump_path, false);
        }
    }
  load_rcfiles();
}

void
app_state::require_working_copy(std::string const & explanation)
{
  N(found_working_copy,
    F("working copy directory required but not found%s%s")
    % (explanation.empty() ? "" : "\n") % explanation);
  write_options();
}

void
app_state::create_working_copy(system_path const & new_dir)
{
  N(!new_dir.empty(), F("invalid directory ''"));

  L(F("creating working copy in %s\n") % new_dir);

  mkdir_p(new_dir);
  go_to_working_copy(new_dir);

  N(!directory_exists(bookkeeping_root),
    F("monotone bookkeeping directory '%s' already exists in '%s'\n")
    % bookkeeping_root % new_dir);

  L(F("creating bookkeeping directory '%s' for working copy in '%s'\n")
    % bookkeeping_root % new_dir);

  mkdir_p(bookkeeping_root);

  make_branch_sticky();

  write_options();

  blank_user_log();

  if (lua.hook_use_inodeprints())
    enable_inodeprints();

  load_rcfiles();
}

void
app_state::set_restriction(path_set const & valid_paths,
                           vector<utf8> const & paths,
                           bool respect_ignore)
{
  static file_path root = file_path_internal("");
  restrictions.clear();
  for (vector<utf8>::const_iterator i = paths.begin(); i != paths.end(); ++i)
    {
      file_path p = file_path_external(*i);

      if (respect_ignore && lua.hook_ignore_file(p))
        {
          L(F("'%s' ignored by restricted path set\n") % p);
          continue;
        }

      N(p == root || valid_paths.find(p) != valid_paths.end(),
        F("unknown path '%s'\n") % p);

      L(F("'%s' added to restricted path set\n") % p);
      restrictions.insert(p);
    }

  // if user supplied a depth but provided no paths
  // assume current directory
  if ((depth != -1) && restrictions.empty())
    {
      restrictions.insert(file_path_external(utf8(".")));
    }
}

bool
app_state::restriction_includes(file_path const & path)
{
  static file_path root = file_path_internal("");
  if (restrictions.empty())
    {
      return true;
    }

  bool user_supplied_depth = (depth != -1);

  // a path that normalizes to "." means that the restriction has been
  // essentially cleared (all files are included). rather than be
  // careful about what goes in to the restricted path set we just
  // check for this special case here.

  if ((!user_supplied_depth) && restrictions.find(root) != restrictions.end())
    {
      return true;
    }

  fs::path test = fs::path(path.as_external(), fs::native);
  long branch_depth = 0;
  long max_depth = depth + 1;

  while (!test.empty())
    {
      L(F("checking restricted path set for '%s'\n") % test.string());

      file_path p = file_path_internal(test.string());
      path_set::const_iterator i = restrictions.find(p);

      if (i != restrictions.end())
        {
          L(F("path '%s' found in restricted path set; '%s' included\n")
            % test.string() % path);
          return true;
        }
      else
        {
          L(F("path '%s' not found in restricted path set; '%s' excluded\n")
            % test.string() % path);
        }

      if (user_supplied_depth && (max_depth == branch_depth)) return false;
      test = test.branch_path();
      ++branch_depth;
    }

  if (user_supplied_depth && (restrictions.find(root) != restrictions.end()))
    {
      return (branch_depth <= max_depth);
    }

  return false;
}

void
app_state::set_database(system_path const & filename)
{
  if (!filename.empty()) db.set_filename(filename);

  options[database_option] = filename.as_internal();
}

void
app_state::set_branch(utf8 const & branch)
{
  branch_name = branch();
}

void
app_state::make_branch_sticky()
{
  options[branch_option] = branch_name();
  if (found_working_copy)
    {
      // already have a working copy, can (must) write options directly,
      // because no-one else will do so
      // if we don't have a working copy yet, then require_working_copy (for
      // instance) will call write_options when it finds one.
      write_options();
    }
}

void
app_state::set_signing_key(utf8 const & key)
{
  internalize_rsa_keypair_id(key, signing_key);

  options[key_option] = key;
}

void
app_state::set_root(system_path const & path)
{
  require_path_is_directory(path,
                            F("search root '%s' does not exist") % path,
                            F("search root '%s' is not a directory\n") % path);
  search_root = path;
  L(F("set search root to %s\n") % search_root);
}

void
app_state::set_message(utf8 const & m)
{
  message = m;
}

void
app_state::set_message_file(utf8 const & m)
{
  message_file = m;
}

void
app_state::set_date(utf8 const & d)
{
  date = d;
}

void
app_state::set_author(utf8 const & a)
{
  author = a;
}

void
app_state::set_depth(long d)
{
  N(d >= 0,
    F("negative depth not allowed\n"));
  depth = d;
}

void
app_state::set_last(long l)
{
  N(l > 0,
    F("negative or zero last not allowed\n"));
  last = l;
}

void
app_state::set_pidfile(system_path const & p)
{
  pidfile = p;
}

void
app_state::add_revision(utf8 const & selector)
{
  revision_selectors.push_back(selector);
}

void
app_state::add_exclude(utf8 const & exclude_pattern)
{
  exclude_patterns.insert(exclude_pattern);
}

void
app_state::set_diff_format(diff_type dtype)
{
  diff_format = dtype;
}

void
app_state::set_diff_args(utf8 const & args)
{
  diff_args_provided = true;
  diff_args = args;
}

void
app_state::set_stdhooks(bool b)
{
  stdhooks = b;
}

void
app_state::set_rcfiles(bool b)
{
  rcfiles = b;
}

void
app_state::set_verbose(bool b)
{
  verbose = b;
}

void
app_state::add_rcfile(utf8 const & filename)
{
  extra_rcfiles.push_back(filename);
}

// rc files are loaded after we've changed to the working copy directory so
// that MT/monotonerc can be loaded between ~/.monotone/monotonerc and other
// rcfiles

void
app_state::load_rcfiles()
{
  // built-in rc settings are defaults

  if (stdhooks)
    lua.add_std_hooks();

  // ~/.monotone/monotonerc overrides that, and
  // MT/monotonerc overrides *that*

  if (rcfiles)
    {
      system_path default_rcfile;
      bookkeeping_path working_copy_rcfile;
      lua.default_rcfilename(default_rcfile);
      lua.working_copy_rcfilename(working_copy_rcfile);
      lua.load_rcfile(default_rcfile, false);
      lua.load_rcfile(working_copy_rcfile, false);
    }

  // command-line rcfiles override even that

  for (vector<utf8>::const_iterator i = extra_rcfiles.begin();
       i != extra_rcfiles.end(); ++i)
    {
      lua.load_rcfile(*i);
    }
}

void
app_state::read_options()
{
  bookkeeping_path o_path;
  get_options_path(o_path);
  try
    {
      if (path_exists(o_path))
        {
          data dat;
          read_data(o_path, dat);
          read_options_map(dat, options);
        }
    }
  catch(std::exception & e)
    {
      W(F("Failed to read options file %s") % o_path);
    }
}

void
app_state::write_options()
{
  bookkeeping_path o_path;
  get_options_path(o_path);
  try
    {
      data dat;
      write_options_map(dat, options);
      write_data(o_path, dat);
    }
  catch(std::exception & e)
    {
      W(F("Failed to write options file %s") % o_path);
    }
}