Logo Search packages:      
Sourcecode: bazaar version File versions

cached-archive.c

/* cached-archive.c:
 *
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 * Copyright (C) 2004 Aaron Bentley
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/vu/vu.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/char/str.h"
#include "hackerlab/bugs/panic.h"

#include "libfsutils/copy-file.h"
#include "libarch/archives.h"
#include "libarch/cached-archive.h"
#include "libarch/arch-cache.h"
#include "libarch/pfs.h"
#include "libawk/trim.h"

static void 
copy_fd_close(int in_fd, int out_fd);
static void cache_close (struct arch_archive * a);
static t_uchar * cache_archive_version (struct arch_archive * a);
static rel_table cache_categories (struct arch_archive * a);
static rel_table cache_branches (struct arch_archive * a, t_uchar * category);
static rel_table cache_versions (struct arch_archive * a, t_uchar * package);
static rel_table cache_revisions (struct arch_archive * a, t_uchar * version);
static t_uchar * cache_archive_log (struct arch_archive * a, t_uchar * revision);
static int cache_revision_type  (enum arch_revision_type * type, int * archive_cached,
                         int *has_ancestry,
                                struct arch_archive * a, t_uchar * revision);
static void cache_get_patch (int out_fd, struct arch_archive * a, t_uchar * revision);
static void cache_get_cached (int out_fd, struct arch_archive * a, t_uchar * revision);
static void cache_get_import (int out_fd, struct arch_archive * a, t_uchar * revision);
static t_uchar * cache_get_continuation (struct arch_archive * a, t_uchar * revision);
static t_uchar * cache_get_meta_info (struct arch_archive * a, t_uchar * meta_info_name);
static int cache_make_category (t_uchar ** errstr, struct arch_archive * a, t_uchar * category);
static int cache_make_branch (t_uchar ** errstr, struct arch_archive * a, t_uchar * branch);
static int cache_make_version (t_uchar ** errstr, struct arch_archive * a, t_uchar * version);
static int cache_lock_revision (t_uchar ** errstr, struct arch_archive * a,
                              t_uchar * version,
                              t_uchar * prev_level,
                              t_uchar * uid,
                              t_uchar * txn_id,
                              t_uchar * new_level);
static int cache_revision_ready (t_uchar ** errstr, struct arch_archive * a,
                               t_uchar * version,
                               t_uchar * prev_level,
                               t_uchar * uid,
                               t_uchar * txn_id,
                               t_uchar * new_level);
static int cache_finish_revision (t_uchar ** errstr, struct arch_archive * a,
                                t_uchar * version,
                                t_uchar * prev_level,
                                t_uchar * uid,
                                t_uchar * txn_id,
                                t_uchar * new_level);
static enum arch_revision_lock_state cache_lock_state (t_uchar ** prev_level_ret,
                                                     t_uchar ** uid_ret,
                                                     t_uchar ** txn_id_ret,
                                                     struct arch_archive * a,
                                                     t_uchar * version);
static int cache_break_revision_lock (t_uchar ** errstr, struct arch_archive * a,
                                    t_uchar * version,
                                    t_uchar * prev_level,
                                    t_uchar * uid,
                                    t_uchar * txn_id);
static int cache_put_log (t_uchar ** errstr, struct arch_archive * a,
                        t_uchar * version,
                        t_uchar * prev_level,
                        t_uchar * uid,
                        t_uchar * txn_id,
                        t_uchar * log_text);
static int cache_put_continuation (t_uchar ** errstr, struct arch_archive * a,
                                 t_uchar * version,
                                 t_uchar * prev_level,
                                 t_uchar * uid,
                                 t_uchar * txn_id,
                                 t_uchar * continuation);
static int cache_put_changeset (t_uchar ** errstr, struct arch_archive * a,
                              t_uchar * version,
                              t_uchar * prev_level,
                              t_uchar * uid,
                              t_uchar * txn_id,
                              t_uchar * level,
                              int in_fd);
static int cache_put_import (t_uchar ** errstr, struct arch_archive * a,
                           t_uchar * version,
                           t_uchar * prev_level,
                           t_uchar * uid,
                           t_uchar * txn_id,
                           t_uchar * level,
                           int in_fd);
static int cache_put_cached (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision, int in_fd);
static int cache_delete_cached (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision);
static void cache_repair_non_txnal (int chatter_fd, struct arch_archive * a);
static void cache_get_ancestry (int out_fd, struct arch_archive * a, t_uchar * revision);
static int cache_put_ancestry (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision, int in_fd);
static int cache_delete_ancestry (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision);


static struct arch_archive_vtable cache_vtable =
{
  "cache",

  cache_close,

  cache_archive_version,

  cache_categories,
  cache_branches,
  cache_versions,
  cache_revisions,

  cache_archive_log,
  cache_revision_type,
  cache_get_patch,
  cache_get_cached,
  cache_get_import,
  cache_get_continuation,
  cache_get_meta_info,

  cache_make_category,
  cache_make_branch,
  cache_make_version,

  cache_lock_revision,
  cache_revision_ready,
  cache_finish_revision,
  cache_break_revision_lock,
  cache_lock_state,

  cache_put_log,
  cache_put_continuation,
  cache_put_changeset,
  cache_put_import,

  cache_put_cached,
  cache_delete_cached,

  cache_repair_non_txnal,

  cache_get_ancestry,
  cache_put_ancestry,
  cache_delete_ancestry
};


/**
 * Constructs a query string for a revision
 * Returns a newly-allocated string
 */
extern t_uchar * 
arch_revision_query (struct arch_archive * a, t_uchar * revision, 
                     t_uchar const * extension)
{
  arch_patch_id patch;
  t_uchar * query = 0;
  
  arch_patch_id_init_archive (&patch, a->official_name, revision);
  
  query =arch_cache_query_new (&patch, extension);

  arch_patch_id_finalise (&patch);
  return query;
}

/**
 * Copies all data from in_fd to out_fd, closes in_fd
 */
static void 
copy_fd_close(int in_fd, int out_fd)
{
  copy_fd (in_fd, out_fd);
  safe_close (in_fd);
}


/**
 * Determines whether the provided location is a cache URL.
 */
int
arch_cached_archive_protocol (t_uchar * location)
{
  if (str_cmp_prefix ("cached:", location) == 0)
    return 1;
  else if (str_cmp_prefix ("uncached:", location) == 0)
    return 0;
  else return (!arch_pfs_is_local (location));
}

/**
 * \brief Ensures a location has a single "uncached:" prefix.
 * \param location The location to convert
 * \return a new string, containing the modified location
 */
extern t_uchar * 
arch_uncached_location (t_uchar *location)
{
  t_uchar * new_location = location;
  while (1)
    {
      if (str_cmp_prefix("cached:", new_location) == 0)
      {
        new_location = new_location + str_length ("cached:");
        continue;
      }
      else if (str_cmp_prefix("uncached:", new_location) == 0)
      {
        new_location = new_location + str_length ("uncached:");
        continue;
      }
      else
        break;
    }
    return str_alloc_cat (0, "uncached:", new_location);
}


/* make a cached archive adaoter and connect,
 * if soft_errors, returns -1 on error
 */
int
arch_cached_archive_connect (struct arch_archive ** answer, int soft_errors)
{
  struct arch_cached_archive * arch = 0;
  t_uchar * wrap_location = 0;
  invariant (arch_cached_archive_protocol ((*answer)->location));
  if (! arch_cache_active ())
    {
      safe_printfmt (2, "No cache attempting to connect to cached archive\n archive: %s\nlocation: %s\n", (*answer)->name, (*answer)->location);
      if (soft_errors)
        return -1;
      else
        exit (2);
    }

  
  arch = lim_realloc (0, (t_uchar *) *answer, sizeof (struct arch_cached_archive));
  *answer = (struct arch_archive *)arch;
  arch->arch.vtable = &cache_vtable;
  
  wrap_location = arch_uncached_location ((*answer)->location);
  arch->wrapped_arch = arch_archive_connect_location ((*answer)->name, wrap_location, 0, soft_errors);
  lim_free (0, wrap_location);
  if (arch->wrapped_arch)
    {
      /* this code section is copied from archive.c's .._connect, which this
       * API is meant to duplicate
       */
      lim_free (0, arch->wrapped_arch->name);
      arch->wrapped_arch->name = str_save (0, (*answer)->name);
      /* not sure about these, from the same block.. 
      answer->refs = 1;
      answer->next = connected_by_name;
      connected_by_name = answer;*/
      return 0;
    }
  
  if (soft_errors)
      return -1;
  else
      exit (2);
}


void
arch_cached_make_archive (t_uchar * name, t_uchar * uri, t_uchar * version, t_uchar *mirror_of, int dot_listing_lossage, int signed_archive)
{
  t_uchar * wrap_location = arch_uncached_location (uri);
  invariant (arch_cached_archive_protocol (uri));
  arch_make_archive (name, wrap_location, mirror_of, dot_listing_lossage, signed_archive, arch_archive_type(version) == arch_archive_tla);
  arch_set_archive_location (name, uri, 1, ARCH_REG_FAIL_NORM);
  lim_free (0, wrap_location);
}


static void cache_close (struct arch_archive * a)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  wrapped_arch->vtable->close (wrapped_arch);
}


static t_uchar * 
cache_archive_version (struct arch_archive * a)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->archive_version (wrapped_arch);
}


static rel_table 
cache_categories (struct arch_archive * a)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->categories (wrapped_arch);
}


static rel_table 
cache_branches (struct arch_archive * a, t_uchar * category)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->branches (wrapped_arch, category);
}


static rel_table 
cache_versions (struct arch_archive * a, t_uchar * package)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->versions (wrapped_arch, package);
}


static rel_table 
cache_revisions (struct arch_archive * a, t_uchar * version)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->revisions (wrapped_arch, version);
}


static t_uchar * 
cache_archive_log (struct arch_archive * a, t_uchar * revision)
{
  t_uchar * query = arch_revision_query (a, revision, "log");
  t_uchar * answer = 0;
  if (!arch_cache_has_answer (query))
    {
      struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
      answer = wrapped_arch->vtable->archive_log (wrapped_arch, revision);
      arch_cache_put_str (query, answer);
    }
  else
    answer = arch_cache_get_str (query);
  lim_free (0, query);
  return answer;
}


static t_uchar * 
arch_revision_type_string (enum arch_revision_type type)
{
  switch (type)
    {
      case arch_import_revision:
        return "import";
      case arch_simple_revision:
        return "simple";
      case arch_continuation_revision:
        return "continuation";
      default:
        ;
    }
  panic ("Unhandled revision type!");
  return 0;
}


static enum arch_revision_type
arch_revision_type_enum (t_uchar * type)
{
  if (str_cmp("import", type) == 0)
    return arch_import_revision;
  if (str_cmp("simple", type) == 0)
    return arch_simple_revision;
  if (str_cmp("continuation", type) == 0)
    return arch_continuation_revision;
  
  panic ("Unhandled revision type!");
  exit (2);
}

/**
 * This is a kinda freaky function, because we can only use the Arch Cache
 * some of the time.
 * If the archive_cached parameter is non-null, we must query the archive,
 * since cacherevs may be added or deleted anytime.
 * ancestry however, may be cached separately, though for now
 * we trest it like archive_cached
 * If we have a full tree in the cache, we force archive_cached to true
 * unless it's an import revision
 */
static int 
cache_revision_type  (enum arch_revision_type * type, int * archive_cached, 
                  int *has_ancestry, 
                      struct arch_archive * a, t_uchar * revision)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  enum arch_revision_type tmp_type;

  t_uchar * type_query = arch_revision_query (a, revision, "type");

  int use_archive = 0;
  int cache_put = 0;
  int answer = -1;

  if (arch_cache_has_answer (type_query))
    {
      cache_put = 0;
      use_archive = (archive_cached != 0 || has_ancestry != 0);
    }
  else
    {
      cache_put = 1;
      use_archive = 1;
    }
  if (use_archive)
    {
      answer = wrapped_arch->vtable->revision_type  (&tmp_type, archive_cached, has_ancestry, wrapped_arch, revision);
    }
  else
    {
      t_uchar * type_str = arch_cache_get_line (type_query);
      tmp_type = arch_revision_type_enum (type_str);
      answer = 0;
      lim_free (0, type_str);
    }
  if (type != 0) 
    *type = tmp_type;
  if (answer == -1)
    return answer;
  
  if (cache_put)
    {
      arch_cache_put_line (type_query, arch_revision_type_string (tmp_type));
    }
  if (archive_cached != 0 && tmp_type != arch_import_revision && !*archive_cached)
    {
      t_uchar * tree_query = arch_revision_query (a, revision, "full-tree.tar.gz");
      if (arch_cache_has_answer (tree_query))
        *archive_cached = 1;
      lim_free (0, tree_query);
    }
  lim_free (0, type_query);
  return answer; 
}


static void 
cache_get_patch (int out_fd, struct arch_archive * a, t_uchar * revision)
{
  t_uchar * query = arch_revision_query (a, revision, "delta.tar.gz");

  if (!arch_cache_has_answer(query))
    {
      struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
      t_uchar * tmp_name;
      int put_fd = arch_cache_put (&tmp_name, query);
      wrapped_arch->vtable->get_patch (put_fd, wrapped_arch, revision);
      safe_close (put_fd);
      arch_cache_commit (tmp_name, query);
      lim_free (0, tmp_name);
    }
  copy_fd_close (arch_cache_get (query), out_fd);
  lim_free (0, query);
}


static void 
cache_get_cached (int out_fd, struct arch_archive * a, t_uchar * revision)
{
  t_uchar * query = arch_revision_query (a, revision, "full-tree.tar.gz");

  if (!arch_cache_has_answer(query))
    {
      struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
      t_uchar * tmp_name;
      int put_fd = arch_cache_put (&tmp_name, query);
      wrapped_arch->vtable->get_cached (put_fd, wrapped_arch, revision);
      safe_close (put_fd);
      arch_cache_commit (tmp_name, query);
      lim_free (0, tmp_name);
    }
  copy_fd_close (arch_cache_get (query), out_fd);
  lim_free (0, query);
}


static void 
cache_get_import (int out_fd, struct arch_archive * a, t_uchar * revision)
{
  t_uchar * query = arch_revision_query (a, revision, "full-tree.tar.gz");

  if (!arch_cache_has_answer(query))
    {
      struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
      t_uchar * tmp_name;
      int put_fd = arch_cache_put (&tmp_name, query);
      wrapped_arch->vtable->get_import(put_fd, wrapped_arch, revision);
      safe_close (put_fd);
      arch_cache_commit (tmp_name, query);
      lim_free (0, tmp_name);
    }
  copy_fd_close (arch_cache_get (query), out_fd);
  lim_free (0, query);
}


static t_uchar * 
cache_get_continuation (struct arch_archive * a, t_uchar * revision)
{
  t_uchar * ancestor_query = arch_revision_query (a, revision, "ancestor");
  t_uchar * continuation = 0;
  if (arch_cache_has_answer (ancestor_query))
    {
      continuation = arch_cache_get_line (ancestor_query);
    }
  else
    {
      struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
      continuation = wrapped_arch->vtable->get_continuation (wrapped_arch, revision);
      continuation = trim_surrounding_ws (continuation);
      arch_cache_put_line (ancestor_query, continuation);
    }
  lim_free (0, ancestor_query);
  return continuation;
}


static t_uchar * 
cache_get_meta_info (struct arch_archive * a, t_uchar * meta_info_name)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->get_meta_info (wrapped_arch, meta_info_name); 
}


static int cache_make_category (t_uchar ** errstr, struct arch_archive * a, t_uchar * category)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->make_category (errstr, wrapped_arch, category);
}


static int cache_make_branch (t_uchar ** errstr, struct arch_archive * a, t_uchar * branch)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->make_branch(errstr, wrapped_arch, branch);
}


static int 
cache_make_version (t_uchar ** errstr, struct arch_archive * a, t_uchar * version)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->make_version(errstr, wrapped_arch, version);
}


static int 
cache_lock_revision (t_uchar ** errstr, struct arch_archive * a,
                     t_uchar * version,
                     t_uchar * prev_level,
                     t_uchar * uid,
                     t_uchar * txn_id,
                     t_uchar * new_level)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->lock_revision (errstr, wrapped_arch, version, prev_level, uid, txn_id, new_level);
}


static int 
cache_revision_ready (t_uchar ** errstr, struct arch_archive * a,
                      t_uchar * version,
                      t_uchar * prev_level,
                      t_uchar * uid,
                      t_uchar * txn_id,
                      t_uchar * new_level)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->revision_ready (errstr, wrapped_arch, version, prev_level, uid, txn_id, new_level);
}


static int 
cache_finish_revision (t_uchar ** errstr, struct arch_archive * a,
                       t_uchar * version,
                       t_uchar * prev_level,
                       t_uchar * uid,
                       t_uchar * txn_id,
                       t_uchar * new_level)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->finish_revision (errstr, wrapped_arch, version, prev_level, uid, txn_id, new_level);
}
static enum arch_revision_lock_state 
cache_lock_state (t_uchar ** prev_level_ret,
                  t_uchar ** uid_ret,
                  t_uchar ** txn_id_ret,
                  struct arch_archive * a,
                  t_uchar * version)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->lock_state (prev_level_ret, uid_ret, txn_id_ret, wrapped_arch, version);
}


static int 
cache_break_revision_lock (t_uchar ** errstr, struct arch_archive * a,
                           t_uchar * version,
                           t_uchar * prev_level,
                           t_uchar * uid,
                           t_uchar * txn_id)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->break_revision_lock(errstr, wrapped_arch, version, prev_level, uid, txn_id);
}


static int 
cache_put_log (t_uchar ** errstr, struct arch_archive * a,
               t_uchar * version,
               t_uchar * prev_level,
               t_uchar * uid,
               t_uchar * txn_id,
               t_uchar * log_text)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->put_log (errstr, wrapped_arch, version, prev_level, uid, txn_id, log_text);
}


static int 
cache_put_continuation (t_uchar ** errstr, struct arch_archive * a,
                        t_uchar * version,
                        t_uchar * prev_level,
                        t_uchar * uid,
                        t_uchar * txn_id,
                        t_uchar * continuation)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->put_continuation (errstr, wrapped_arch, version, prev_level, uid, txn_id, continuation);
}


static int 
cache_put_changeset (t_uchar ** errstr, struct arch_archive * a,
                     t_uchar * version,
                     t_uchar * prev_level,
                     t_uchar * uid,
                     t_uchar * txn_id,
                     t_uchar * level,
                     int in_fd)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->put_changeset (errstr, wrapped_arch, version, prev_level, uid, txn_id, level, in_fd);
}


static int 
cache_put_import (t_uchar ** errstr, struct arch_archive * a,
                  t_uchar * version,
                  t_uchar * prev_level,
                  t_uchar * uid,
                  t_uchar * txn_id,
                  t_uchar * level,
                  int in_fd)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->put_import (errstr, wrapped_arch, version, prev_level, uid, txn_id, level, in_fd);
}


static int 
cache_put_cached (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision, int in_fd)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->put_cached (errstr, wrapped_arch, revision, in_fd);
}


static int 
cache_delete_cached (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->delete_cached (errstr, wrapped_arch, revision);
}


static void 
cache_repair_non_txnal (int chatter_fd, struct arch_archive * a)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  wrapped_arch->vtable->repair_non_txnal (chatter_fd, wrapped_arch);
}

void 
cache_get_ancestry (int out_fd, struct arch_archive * a, t_uchar * revision)
{
    /* FIXME: stash this in the cache */
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->get_ancestry (out_fd, wrapped_arch, revision);
}

static int 
cache_put_ancestry (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision, int in_fd)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->put_ancestry (errstr, wrapped_arch, revision, in_fd);
}


static int 
cache_delete_ancestry (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision)
{
  struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
  return wrapped_arch->vtable->delete_ancestry (errstr, wrapped_arch, revision);
}

struct arch_archive *
arch_cached_archive_as_pfs(struct arch_archive * arch)
{
    struct arch_archive *result;
    invariant (!str_cmp (arch->vtable->type, "cache"));
    result = ((struct arch_cached_archive *)arch)->wrapped_arch;
    invariant (!str_cmp (result->vtable->type, "pfs"));
    return result;
}

/* tag: b6a409d5-c4b2-48ee-bdb4-5c2eeebb2bc6 
 */

Generated by  Doxygen 1.6.0   Back to index