Logo Search packages:      
Sourcecode: bazaar version File versions

archive.c

/* archive.c:
 *
 * vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "config-options.h"
#include "hackerlab/bugs/panic.h"
#include "hackerlab/os/errno.h"
#include "hackerlab/os/errno-to-string.h"
#include "hackerlab/os/sys/types.h"
#include "hackerlab/os/sys/wait.h"
#include "hackerlab/os/signal.h"
#include "hackerlab/os/time.h"
#include "hackerlab/os/unistd.h"
#include "hackerlab/os/stdlib.h"
#include "hackerlab/fmt/cvt.h"
#include "hackerlab/mem/alloc-limits.h"
#include "hackerlab/mem/mem.h"
#include "hackerlab/hash/hash-utils.h"
#include "hackerlab/arrays/ar.h"
#include "hackerlab/char/str.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/fs/cwd.h"
#include "hackerlab/fs/file-names.h"
#include "libfsutils/tmp-files.h"
#include "libfsutils/rmrf.h"
#include "libfsutils/copy-file.h"
#include "libawk/trim.h"
#include "libarch/namespace.h"
#include "libarch/my.h"
#include "libarch/exec.h"
#include "libarch/archives.h"
#include "libarch/archive-version.h"
#include "libarch/pfs.h"
#include "libarch/archive-pfs.h"
#include "libarch/cached-archive.h"
#include "libarch/arch-cache.h"
#include "libarch/archive.h"


/* __STDC__ prototypes for static functions */
static int invoke_tar_extract (int * pid_ret);
static int wait_for_tar (int pid);
static void ensure_writable (struct arch_archive * arch, int mirror_ok);



static struct arch_archive * connected_by_name = 0;



void
arch_make_archive (t_uchar * name, t_uchar * location, t_uchar * mirror_of, int dot_listing_lossage, int signed_archive, int tla_archive)
{
  t_uchar * current_loc = 0;
  t_uchar * version;

  invariant (arch_valid_archive_name (name));

  current_loc = arch_archive_location (name, 1);

  if (current_loc)
    {
      safe_printfmt (2, "arch_make_archive: archive already registered\n  name: %s\n", name);
      exit (2);
    }

  version = arch_archive_version_for_new_archive (tla_archive);

  if (arch_cached_archive_protocol (location))
    arch_cached_make_archive (name, location, version, mirror_of, dot_listing_lossage, signed_archive);
  else
    {
      if (str_cmp_prefix ("uncached:", location) == 0)
        location = location + str_length ("uncached:");
      arch_pfs_make_archive (name, location, version, mirror_of, dot_listing_lossage, signed_archive);
    }

  arch_run_hook ("make-archive", "ARCH_ARCHIVE", name, "ARCH_LOCATION", location, 0);

  lim_free (0, version);
}

t_uchar *
arch_archive_not_writable (struct arch_archive *arch, int mirror_ok)
{
  t_uchar * failure = 0;
  t_uchar * success = 0;

  if (arch->access != arch_archive_writable)
   {
     failure = str_alloc_cat_many  (0, "attempt to modify archive that has ",
                                    "read-only compatibility: ",
                                    arch->name,
                                    "\n",
                                    str_end);
      return failure;
    }

  if (!mirror_ok)
    {
      if (!arch->client_anticipates_mirror && !!arch->mirror_of)
        {
          failure = str_alloc_cat_many (0, "attempt to write directly to mirror\n",
                                        "  archive: ", arch->name, "\n",
                                        "mirror of: ", arch->mirror_of, "\n",
                                        str_end);
          return failure;
        }

      if (arch->client_anticipates_mirror && !arch->mirror_of)
        {
          failure = str_alloc_cat_many(0, "archive is not a mirror of ",
                                       arch->client_anticipates_mirror, "\n",
                                       "   archive: ", arch->name, "\n",
                                       "   expected mirror of",
                                       arch->client_anticipates_mirror, "\n",
                                       "\n",
                                       str_end);
          return failure;
        }

      if (arch->client_anticipates_mirror && str_cmp (arch->client_anticipates_mirror, arch->mirror_of))
        {
          failure = str_alloc_cat_many(0, "attempt to write to wrong mirror\n",
                                       "   archive: ", arch->name, "\n",
                                       "   expected mirror:",
                                       arch->client_anticipates_mirror, "\n",
                                       "   got mirror of: ", arch->mirror_of, "\n",
                                       str_end);
          return failure;
        }
    }
  return success;
}

struct arch_archive *
arch_archive_connect_location (t_uchar * name, t_uchar * location, t_uchar * want_mirror_of, int soft_errors)
{
  struct arch_archive * answer;
  int err;

  answer = lim_malloc (0, sizeof (*answer));
  mem_set0 ((t_uchar *)answer, sizeof (*answer));

  answer->name = str_save (0, name);

  answer->client_anticipates_mirror = (want_mirror_of ? str_save (0, want_mirror_of) : 0);

  int cached_protocol = arch_cached_archive_protocol (location);

  if (cached_protocol)
      arch_ensure_cache_path ();
  if (cached_protocol && arch_cache_active ())
    {
      answer->location = str_save (0, location);
      err = arch_cached_archive_connect (&answer, soft_errors);
    }
  else
    {
      t_uchar * uncached_location = arch_uncached_location(location);
      answer->location = str_save (0, uncached_location + str_length ("uncached:"));
      lim_free (0, uncached_location);
      err = arch_pfs_archive_connect (&answer, soft_errors);
    }
  
  if (err)
    {
      if (soft_errors)
        {
          lim_free (0, answer);
          return NULL;
        }
      panic ("failed to connect to location.");
    }

  answer->version = answer->vtable->archive_version (answer);
  answer->access = arch_archive_access (answer->version);
  answer->type = arch_archive_type (answer->version);

  if (answer->access == arch_archive_incompatible)
    {
      safe_printfmt (2, "arch_archive_connect: attempt to connect to incompatible archive\n  archive: %s\n", (name ? name : location));
      if (soft_errors)
        {
          lim_free (0, answer);
          return NULL;
        }
      else
          exit (2);
    }

  answer->mirror_of = trim_surrounding_ws (arch_get_meta_info (answer, "mirror"));
  answer->official_name = trim_surrounding_ws (arch_get_meta_info (answer, "name"));
  if (!answer->official_name)
    {
      int ign;

      printfmt (&ign, 2, "Unable to read the official name of archive %s\n  Archive damaged?\n", name);
      if (soft_errors)
        {
          lim_free (0, answer);
          return NULL;
        }
      else
          exit (2);
    }
  lim_free (0, answer->name);
  answer->name = str_save (0, answer->official_name);
  answer->http_blows = arch_get_meta_int_info (answer, "http-blows");
  answer->signed_archive = arch_get_meta_int_info (answer, "signed-archive");

  if (want_mirror_of && str_cmp (want_mirror_of, answer->mirror_of))
    {
      safe_printfmt (2, "arch_archive_connect: attempt to connect to wrong mirror\n  archive: %s\n  wanted mirror of: %s\n  got mirror of: %s\n",
                     (name ? name : location), want_mirror_of, (answer->mirror_of ? answer->mirror_of : (t_uchar *)"(not a mirror)"));
      if (soft_errors)
        {
          lim_free (0, answer);
          /* FIXME: this leaks, we should be calling the freememory function .. */
          return NULL;
        }
      else
          exit (2);
    }

  return answer;
}

struct arch_archive *
arch_archive_connect (t_uchar * name, t_uchar * want_mirror_of)
{
    return arch_archive_connect_ext (name, want_mirror_of, 0);
}

struct arch_archive *
arch_archive_connect_ext (t_uchar * name, t_uchar * want_mirror_of, int soft_errors)
{
  struct arch_archive * answer = 0;

  for (answer = connected_by_name;
       answer && str_cmp (answer->name, name);
       answer = answer->next)
    ;

  if (answer)
    {
      if (want_mirror_of && str_cmp (want_mirror_of, answer->mirror_of))
        {
          safe_printfmt (2, "arch_archive_connect: attempt to connect to wrong mirror\n  archive: %s\n  wanted mirror of: %s\n  got mirror of: %s\n",
                         name, want_mirror_of, (answer->mirror_of ? answer->mirror_of : (t_uchar *)"(not a mirror)"));
          exit (2);
        }

      ++answer->refs;
      return answer;
    }
  else
    {
      t_uchar * location = 0;

      location = arch_archive_location (name, 0);

      answer = arch_archive_connect_location (name, location, want_mirror_of, soft_errors);
      if (!answer)
        {
          if (soft_errors)
            {
              lim_free (0, location);
              return NULL;
            }
          panic ("failed to connect to location with soft_errors zero");
        }
      lim_free (0, answer->name);
      answer->name = str_save (0, name);
      answer->refs = 1;
      answer->next = connected_by_name;
      connected_by_name = answer;

      lim_free(0, location);
      return answer;
    }
}


void
arch_archive_close (struct arch_archive * arch)
{
  if (!arch)
    return;

  if (arch->refs)
    --arch->refs;

  if (!arch->refs)
    {
      struct arch_archive ** handle = &connected_by_name;

      while (*handle)
        {
          if (*handle != arch)
            handle = &((*handle)->next);
          else
            {
              *handle = (*handle)->next;
            }
        }

      arch->vtable->close (arch);
      lim_free (0, arch->name);
      lim_free (0, arch->location);
      lim_free (0, arch->version);
      lim_free (0, arch);
    }
}


t_uchar *
arch_archive_version (struct arch_archive * arch)
{
  return arch->vtable->archive_version (arch);
}


rel_table
arch_archive_categories (struct arch_archive * arch)
{
  rel_table answer = 0;

  answer = arch->vtable->categories (arch);
  rel_sort_table_by_field (0, answer, 0);

  return answer;
}


rel_table
arch_archive_branches (struct arch_archive * arch, t_uchar * category)
{
  rel_table answer = 0;

  answer = arch->vtable->branches (arch, category);
  rel_sort_table_by_field (0, answer, 0);

  return answer;
}


rel_table
arch_archive_versions (struct arch_archive * arch, t_uchar * package)
{
  rel_table answer = 0;

  answer = arch->vtable->versions (arch, package);
  arch_sort_table_by_name_field (0, answer, 0);

  return answer;
}

t_uchar *
arch_archive_latest_revision (struct arch_archive * arch, t_uchar * unknown_component, int full)
{
  rel_table revs = 0;
  t_uchar * answer = 0;
  t_uchar * version = 0;

  /* First, see if we were just given a version */
  if ( arch_valid_package_name ( unknown_component, arch_no_archive, arch_req_version, 0))
    {
      version = str_save(0, unknown_component);
    }

  /* Ok. Not a version. Lets see if its a package */
  else if (arch_valid_package_name (unknown_component, arch_no_archive, arch_req_package, 0))
    {
      rel_table versions = 0;

      versions = arch_archive_versions (arch, unknown_component);
      arch_sort_table_by_name_field (1, versions, 0);
      if (! versions)
        {
          safe_printfmt (2, "arch_archive_latest_revision: ");
          safe_printfmt (2, "package %s has no versions\n", unknown_component);
          exit(1);
        }
     
      version = str_save(0, versions[0][0]);
    }

  /* Uh oh. we were given nothing we can use */
  else
    {
       safe_printfmt(2, "arch_archive_versions: %s neither package nor version.\n",
                     unknown_component);
       exit(1);
    }

  revs = arch_archive_revisions (arch, version, full);

  if (revs)
    answer = str_save (0, revs[rel_n_records (revs) - 1][0]);

  rel_free_table (revs);
  lim_free (0, version);
  return answer;
}


rel_table
arch_archive_revisions (struct arch_archive * arch, t_uchar * version, int full)
{
  rel_table answer = 0;

  answer = arch->vtable->revisions (arch, version);
  arch_sort_table_by_patch_level_field (0, answer, 0);

  if (full)
    {
      t_uchar * fqv = 0;
      int x;

      if (full == 1)
        fqv = arch_fully_qualify (arch->name, version);
      else
        fqv = str_save (0, version);

      for (x = 0; x < rel_n_records (answer); ++x)
        {
          t_uchar * t = 0;

          t = answer[x][0];
          answer[x][0] = str_alloc_cat_many (0, fqv, "--", t, str_end);
          lim_free (0, t);
        }

      lim_free (0, fqv);
    }

  return answer;
}

t_uchar *
arch_archive_log (struct arch_archive * arch, t_uchar * revision)
{
  t_uchar * answer = 0;

  answer = arch->vtable->archive_log (arch, revision);

  return answer;
}

/* RBC 20050109 the api here needs an overhaul.
 * is_cached should rather be a bitfield or just return into
 * a struct
 */
void
arch_revision_type (enum arch_revision_type * type, int * is_cached, int *has_ancestry, 
                   struct arch_archive * arch, t_uchar * revision)
{
  if (arch->vtable->revision_type (type, is_cached, has_ancestry, arch, revision) < 0)
    {
      safe_printfmt (2, "failed to query archive:\n  name: %s\n  location: %s\n  revision: %s\n", arch->name, arch->location, revision);
      exit (2);
    }
}

int
arch_revision_exists (struct arch_archive * arch, t_uchar * revision)
{
  enum arch_revision_type type;
  return (arch->vtable->revision_type (&type, NULL, NULL, arch, revision) >= 0);
}


void
arch_get_patch_targz (int out_fd, struct arch_archive * arch, t_uchar * revision)
{
  arch->vtable->get_patch (out_fd, arch, revision);
}

void
arch_get_patch (struct arch_archive * arch, t_uchar * revision, t_uchar * dest_dir)
{
  int here_fd;
  t_uchar * dest_dir_dir = 0;
  t_uchar * dest_dir_dir_path = 0;
  t_uchar * dest_dir_tail = 0;
  t_uchar * dest_dir_path = 0;
  t_uchar * tmpdir_path = 0;
  int out_fd = 0;
  int tar_pid = 0;
  t_uchar * patches_file_name = 0;
  t_uchar * patches_file_dest = 0;

  here_fd = safe_open (".", O_RDONLY, 0);

  dest_dir_dir = file_name_directory_file (0, dest_dir);
  if (dest_dir_dir)
    safe_chdir (dest_dir_dir);
  dest_dir_dir_path = safe_current_working_directory ();
  dest_dir_tail = file_name_tail (0, dest_dir);
  dest_dir_path = file_name_in_vicinity (0, dest_dir_dir_path, dest_dir_tail);

  tmpdir_path = tmp_file_name (dest_dir_dir_path, ",,get-patch");
  patches_file_name = str_alloc_cat (0, revision, ".patches");
  patches_file_dest = file_name_in_vicinity (0, "..", dest_dir_tail);

  safe_mkdir (tmpdir_path, 0777);
  safe_chdir (tmpdir_path);

  out_fd = invoke_tar_extract (&tar_pid);
  arch->vtable->get_patch (out_fd, arch, revision);
  safe_close (out_fd);
  if (wait_for_tar (tar_pid))
    panic ("arch_get_patch: tar exitted with non-0 status");
  safe_rename (patches_file_name, patches_file_dest);

  safe_fchdir (here_fd);
  safe_close (here_fd);
  rmrf_file (tmpdir_path);

  lim_free (0, dest_dir_dir);
  lim_free (0, dest_dir_dir_path);
  lim_free (0, dest_dir_tail);
  lim_free (0, dest_dir_path);
  lim_free (0, tmpdir_path);
  lim_free (0, patches_file_name);
  lim_free (0, patches_file_dest);
}

void
arch_get_cached_revision_targz (int out_fd, struct arch_archive * arch, t_uchar * revision)
{
  arch->vtable->get_cached (out_fd, arch, revision);
}

void
arch_get_cached_revision (struct arch_archive * arch, t_uchar * revision, t_uchar * dest_dir)
{
  int here_fd;
  t_uchar * dest_dir_dir = 0;
  t_uchar * dest_dir_dir_path = 0;
  t_uchar * dest_dir_tail = 0;
  t_uchar * dest_dir_path = 0;
  t_uchar * tmpdir_path = 0;
  int out_fd = 0;
  int tar_pid = 0;
  t_uchar * patches_file_dest = 0;

  here_fd = safe_open (".", O_RDONLY, 0);

  dest_dir_dir = file_name_directory_file (0, dest_dir);
  if (dest_dir_dir)
    safe_chdir (dest_dir_dir);
  dest_dir_dir_path = safe_current_working_directory ();
  dest_dir_tail = file_name_tail (0, dest_dir);
  dest_dir_path = file_name_in_vicinity (0, dest_dir_dir_path, dest_dir_tail);

  tmpdir_path = tmp_file_name (dest_dir_dir_path, ",,get-patch");
  patches_file_dest = file_name_in_vicinity (0, "..", dest_dir_tail);

  safe_mkdir (tmpdir_path, 0777);
  safe_chdir (tmpdir_path);

  out_fd = invoke_tar_extract (&tar_pid);
  arch->vtable->get_cached (out_fd, arch, revision);
  safe_close (out_fd);
  if (wait_for_tar (tar_pid))
    panic ("arch_get_patch: tar exitted with non-0 status");
  safe_rename (revision, patches_file_dest);

  safe_fchdir (here_fd);
  safe_close (here_fd);
  rmrf_file (tmpdir_path);

  lim_free (0, dest_dir_dir);
  lim_free (0, dest_dir_dir_path);
  lim_free (0, dest_dir_tail);
  lim_free (0, dest_dir_path);
  lim_free (0, tmpdir_path);
  lim_free (0, patches_file_dest);
}

void
arch_get_import_targz (int out_fd, struct arch_archive * arch, t_uchar * revision)
{
  arch->vtable->get_import (out_fd, arch, revision);
}


void
arch_get_import_revision (struct arch_archive * arch, t_uchar * revision, t_uchar * dest_dir)
{
  int here_fd;
  t_uchar * dest_dir_dir = 0;
  t_uchar * dest_dir_dir_path = 0;
  t_uchar * dest_dir_tail = 0;
  t_uchar * dest_dir_path = 0;
  t_uchar * tmpdir_path = 0;
  int out_fd = 0;
  int tar_pid = 0;
  t_uchar * patches_file_dest = 0;

  here_fd = safe_open (".", O_RDONLY, 0);

  dest_dir_dir = file_name_directory_file (0, dest_dir);
  if (dest_dir_dir)
    safe_chdir (dest_dir_dir);
  dest_dir_dir_path = safe_current_working_directory ();
  dest_dir_tail = file_name_tail (0, dest_dir);
  dest_dir_path = file_name_in_vicinity (0, dest_dir_dir_path, dest_dir_tail);

  tmpdir_path = tmp_file_name (dest_dir_dir_path, ",,get-patch");
  patches_file_dest = file_name_in_vicinity (0, "..", dest_dir_tail);

  safe_mkdir (tmpdir_path, 0777);
  safe_chdir (tmpdir_path);

  out_fd = invoke_tar_extract (&tar_pid);
  arch->vtable->get_import (out_fd, arch, revision);
  safe_close (out_fd);
  if (wait_for_tar (tar_pid))
    panic ("arch_get_patch: tar exitted with non-0 status");
  safe_rename (revision, patches_file_dest);

  safe_fchdir (here_fd);
  safe_close (here_fd);
  rmrf_file (tmpdir_path);

  lim_free (0, dest_dir_dir);
  lim_free (0, dest_dir_dir_path);
  lim_free (0, dest_dir_tail);
  lim_free (0, dest_dir_path);
  lim_free (0, tmpdir_path);
  lim_free (0, patches_file_dest);
}


t_uchar *
arch_get_continuation (struct arch_archive * arch, t_uchar * revision)
{
  t_uchar * raw_data = 0;
  t_uchar * start;
  t_uchar * end;
  t_uchar * answer = 0;

  raw_data = arch->vtable->get_continuation (arch, revision);

  start = raw_data;
  while (*start && char_is_space (*start))
    ++start;

  end = start;
  while (*end && !char_is_space (*end))
    ++end;

  answer = str_save_n (0, start, end - start);

  lim_free (0, raw_data);

  return answer;
}

t_uchar *
arch_get_meta_info (struct arch_archive * arch, t_uchar * meta_info_name)
{
  t_uchar * answer = 0;

  answer = arch->vtable->get_meta_info (arch, meta_info_name);

  return answer;
}


int
arch_make_category (t_uchar ** errstr,
                    struct arch_archive * arch, t_uchar * category)
{
  int answer;

  ensure_writable (arch, 0);
  answer = arch->vtable->make_category (errstr, arch, category);
  arch_run_hook ("make-category", "ARCH_ARCHIVE", arch->name, "ARCH_CATEGORY", category, 0);
  return answer;
}

int
arch_make_branch (t_uchar ** errstr,
                    struct arch_archive * arch, t_uchar * branch)
{
  int answer;

  ensure_writable (arch, 0);
  answer = arch->vtable->make_branch (errstr, arch, branch);
  arch_run_hook ("make-branch", "ARCH_ARCHIVE", arch->name, "ARCH_BRANCH", branch, 0);
  return answer;
}

int
arch_make_version (t_uchar ** errstr,
                    struct arch_archive * arch, t_uchar * version)
{
  int answer;

  ensure_writable (arch, 0);
  answer = arch->vtable->make_version (errstr, arch, version);
  arch_run_hook ("make-version", "ARCH_ARCHIVE", arch->name, "ARCH_VERSION", version, 0);
  return answer;
}




int
arch_archive_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)
{
  t_uchar *local_id = 0;
  int result;
  ensure_writable (a, 1);
  if (!txn_id)
    {
      local_id = arch_generate_txn_id ();
      txn_id = local_id;
    }
  result = a->vtable->lock_revision (errstr, a, version, prev_level, uid, txn_id, new_level);
  lim_free (0, local_id);
  return result;
}


int
arch_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)
{
  return a->vtable->revision_ready (errstr, a, version, prev_level, uid, txn_id, new_level);
}



int
arch_archive_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)
{
  ensure_writable (a, 0);
  return a->vtable->finish_revision (errstr, a, version, prev_level, uid, txn_id, new_level);
}

enum arch_revision_lock_state
arch_archive_revision_lock_state (t_uchar ** prev_level_ret,
                                  t_uchar ** uid_ret,
                                  t_uchar ** txn_id_ret,
                                  struct arch_archive * a,
                                  t_uchar * version)
{
  ensure_writable (a, 1);
  return a->vtable->lock_state (prev_level_ret, uid_ret, txn_id_ret, a, version);
}

int
arch_archive_break_revision_lock (t_uchar ** errstr, struct arch_archive * a,
                                  t_uchar * version,
                                  t_uchar * prev_level,
                                  t_uchar * uid,
                                  t_uchar * txn_id)
{
  ensure_writable (a, 1);
  return a->vtable->break_revision_lock (errstr, a, version, prev_level, uid, txn_id);
}




int
arch_archive_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)
{
  ensure_writable (a, 0);
  return a->vtable->put_log (errstr, a, version, prev_level, uid, txn_id, log_text);
}



int
arch_archive_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)
{
  ensure_writable (a, 0);
  return a->vtable->put_continuation (errstr, a, version, prev_level, uid, txn_id, continuation);
}


int
arch_archive_put_changeset_targz (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)
{
  int answer;

  answer = a->vtable->put_changeset (errstr, a, version, prev_level, uid, txn_id, level, in_fd);

  return answer;
}


int
arch_archive_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,
                            t_uchar * dir)
{
  t_uchar * dir_tail = 0;
  t_uchar * desired_name = 0;
  t_uchar * tar_file_path = 0;
  int in_fd;
  int answer;

  dir_tail = file_name_tail (0, dir);
  desired_name = str_alloc_cat_many (0, version, "--", level, ".patches", str_end);
  invariant (!str_cmp (dir_tail, desired_name));


  /* GNU tar 1.13 is busted and doesn't output to pipes correctly.
   */

  ensure_writable (a, 0);

  tar_file_path = make_tmp_tar_archive (dir);

  in_fd = safe_open (tar_file_path, O_RDONLY, 0);

  answer = a->vtable->put_changeset (errstr, a, version, prev_level, uid, txn_id, level, in_fd);

  safe_close (in_fd);
  safe_unlink (tar_file_path);

  lim_free (0, dir_tail);
  lim_free (0, desired_name);
  lim_free (0, tar_file_path);

  return answer;
}


int
arch_archive_put_import_targz (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)
{
  int answer;

  answer = a->vtable->put_import (errstr, a, version, prev_level, uid, txn_id, level, in_fd);

  return answer;
}


int
arch_archive_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,
                         t_uchar * dir)
{
  t_uchar * dir_tail = 0;
  t_uchar * desired_name = 0;
  t_uchar * tar_file_path = 0;
  int in_fd;
  int answer;

  dir_tail = file_name_tail (0, dir);
  desired_name = str_alloc_cat_many (0, version, "--", level, str_end);
  invariant (!str_cmp (dir_tail, desired_name));


  /* GNU tar 1.13 is busted and doesn't output to pipes correctly.
   */

  ensure_writable (a, 0);

  tar_file_path = make_tmp_tar_archive (dir);

  in_fd = safe_open (tar_file_path, O_RDONLY, 0);

  answer = a->vtable->put_import (errstr, a, version, prev_level, uid, txn_id, level, in_fd);

  safe_close (in_fd);
  safe_unlink (tar_file_path);

  lim_free (0, dir_tail);
  lim_free (0, desired_name);
  lim_free (0, tar_file_path);

  return answer;
}

int
arch_archive_put_cached_targz (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision, int in_fd)
{
  int answer;

  answer = a->vtable->put_cached (errstr, a, revision, in_fd);

  return answer;
}

int
arch_archive_put_cached (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision, t_uchar * dir)
{
  t_uchar * dir_tail = 0;
  t_uchar * tar_file_path = 0;
  int in_fd;
  int answer;

  dir_tail = file_name_tail (0, dir);
  invariant (!str_cmp (dir_tail, revision));


  /* GNU tar 1.13 is busted and doesn't output to pipes correctly.
   */

  ensure_writable (a, 1);

  tar_file_path = make_tmp_tar_archive (dir);

  in_fd = safe_open (tar_file_path, O_RDONLY, 0);

  answer = a->vtable->put_cached (errstr, a, revision, in_fd);

  safe_close (in_fd);
  safe_unlink (tar_file_path);

  lim_free (0, dir_tail);
  lim_free (0, tar_file_path);

  return answer;
}


int
arch_archive_delete_cached (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision)
{
  ensure_writable (a, 1);
  return a->vtable->delete_cached (errstr, a, revision);
}

void
arch_archive_repair_non_txnal (int chatter_fd, struct arch_archive * a)
{
  ensure_writable (a, 1);
  a->vtable->repair_non_txnal (chatter_fd, a);
}

void
arch_archive_get_ancestry (int out_fd, struct arch_archive * arch, t_uchar * revision)
{
  arch->vtable->get_ancestry (out_fd, arch, revision);
}

int
arch_archive_put_ancestry (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision, int in_fd)
{
  int answer;

  answer = a->vtable->put_ancestry (errstr, a, revision, in_fd);
  
  return answer;
}

int
arch_archive_delete_ancestry (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision)
{
  ensure_writable (a, 1);
  return a->vtable->delete_ancestry (errstr, a, revision);
}



t_uchar *
arch_generate_txn_id (void)
{
  time_t now;
  pid_t pid;
  char hostname[64];
  char number[64];
  t_uchar * answer;

  now = time (0);
  pid = getpid ();

  mem_set0 ((t_uchar *)hostname, sizeof (hostname));
  gethostname (hostname, sizeof (hostname));

  cvt_ulong_to_hex (number, hash_ul((t_ulong)now ^ (t_ulong)pid));
  answer = str_save (0, number);

  cvt_ulong_to_hex (number, hash_mem ((t_uchar *)hostname, sizeof (hostname)));
  answer = str_realloc_cat (0, answer, number);

  cvt_ulong_to_hex (number, hash_ul ((t_ulong)now ^ ~(t_ulong)pid));
  answer = str_realloc_cat (0, answer, number);

  return answer;
}


t_uchar *
arch_previous_revision (struct arch_archive * arch, t_uchar * revision)
{
  t_uchar * version = 0;
  t_uchar * patch_level = 0;
  enum arch_patch_level_type current_type;
  enum arch_patch_level_type next_type;
  t_ulong current_n;
  t_ulong next_n;
  t_uchar * next_patch_level = 0;
  t_uchar * answer = 0;


  version = arch_parse_package_name (arch_ret_package_version, 0, revision);
  patch_level = arch_parse_package_name (arch_ret_patch_level, 0, revision);

  current_type = arch_analyze_patch_level (&current_n, patch_level);

  if (current_type == arch_is_base0_level)
    {
      answer = 0;
    }
  else
    {
      if (((current_type == arch_is_patch_level) || (current_type == arch_is_versionfix_level)) && (current_n > 1))
        {
          next_type = current_type;
          next_n = current_n - 1;
        }
      else if (current_n == 1)
        {
          next_n = 0;
          if (current_type == arch_is_patch_level)
            next_type = arch_is_base0_level;
          else
            next_type = arch_is_version_level;
        }
      else
        {
          rel_table revisions = 0;
          int x;

          revisions = arch_archive_revisions (arch, version, 0);

          if (!revisions)
            {
              safe_printfmt (2, "version has no revisions (%s/%s)\n", arch->name, version);
              exit (2);
            }

          for (x = 0; x < rel_n_records (revisions); ++x)
            if (revisions[x][0][0] == 'v')
              break;

          invariant (x);

          if (x == rel_n_records (revisions))
            x = rel_n_records (revisions);

          if (x == 1)
            {
              next_type = arch_is_base0_level;
              next_n = 0;
            }
          else
            {
              next_type = arch_is_patch_level;
              next_n = x - 1;
            }

          rel_free_table (revisions);
        }

      next_patch_level = arch_form_patch_level (next_type, next_n);
      answer = str_alloc_cat_many (0, version, "--", next_patch_level, str_end);
    }

  lim_free (0, version);
  lim_free (0, patch_level);
  lim_free (0, next_patch_level);

  return answer;
}


t_uchar *
arch_ancestor_revision (struct arch_archive * arch, t_uchar * revision)
{
  enum arch_revision_type type;

  arch_revision_type (&type, NULL, NULL, arch, revision);

  switch (type)
    {
    default:
      {
        panic ("arch_ancestor_revision: unrecognized revision type");
        return 0;
      }
    case arch_import_revision:
      {
        return 0;
      }
    case arch_simple_revision:
      {
        t_uchar * prev_revision = 0;
        t_uchar * answer = 0;

        prev_revision = arch_previous_revision (arch, revision);
        answer = arch_fully_qualify (arch->name, prev_revision);

        lim_free (0, prev_revision);

        return answer;
      }
    case arch_continuation_revision:
      {
        return arch_get_continuation (arch, revision);
      }
    }
}


/* this creates a temporary file name for use in archive 
 * operations.
 */
t_uchar *
archive_tmp_file_name (t_uchar * dir, t_uchar * basename)
{
  t_uchar * my_uid = 0;
  t_uchar * tmp_name = 0;

  my_uid = arch_my_id_uid_default ("Unknown User <example@example.com>");
  tmp_name = tmp_file_name (dir, basename);

  tmp_name = str_realloc_cat_many (0, tmp_name, ".", my_uid, str_end);

  lim_free (0, my_uid);

  return tmp_name;
}




static int
invoke_tar_extract (int * pid_ret)
{
  int pipe_fds[2];
  int pid;

  if (pipe (pipe_fds))
    panic ("unable to create pipe fds for tar");

  pid = fork ();

  if (pid == -1)
    panic ("unable to fork for patch");

  if (pid)
    {
      *pid_ret = pid;
      safe_close (pipe_fds[0]);
      return pipe_fds[1];
    }
  else
    {
      t_uchar ** argv;

      safe_close (pipe_fds[1]);

      argv = 0;

      *(t_uchar **)ar_push ((void **)&argv, 0, sizeof (char *)) = cfg__gnu_tar;
      *(t_uchar **)ar_push ((void **)&argv, 0, sizeof (char *)) = "-m";
      *(t_uchar **)ar_push ((void **)&argv, 0, sizeof (char *)) = "--preserve";
      *(t_uchar **)ar_push ((void **)&argv, 0, sizeof (char *)) = "-zxf";
      *(t_uchar **)ar_push ((void **)&argv, 0, sizeof (char *)) = "-";
      *(t_uchar **)ar_push ((void **)&argv, 0, sizeof (char *)) = 0;

      safe_move_fd (pipe_fds[0], 0);

      arch_util_execvp (cfg__gnu_tar, argv);
      panic ("invoke_tar_extract: execvp for patch returned to caller");
      exit (2);
    }
  panic ("invoke_tar_extract: not reached");
  return -1;
}

static int
wait_for_tar (int pid)
{
  int status;
  int wait_pid;

  wait_pid = waitpid (pid, &status, 0);
  if (wait_pid < 0)
    {
      panic_msg ("error waiting for tar subprocess");
      kill (0, SIGKILL);
      panic ("error waiting for subprocess");
    }
  if (WIFSIGNALED (status))
    {
      safe_printfmt (2, "\n");
      safe_printfmt (2, "wait_for_tar: tar subprocess killed by signal %d\n", WTERMSIG (status));
      safe_printfmt (2, "\n");
      exit (2);
      return -1;
    }
  else if (!WIFEXITED (status))
    {
      panic_msg ("waitpid returned for a non-exited process");
      kill (0, SIGKILL);
      panic ("waitpid returned for a non-exited process");
      return -1;
    }
  else
    {
      int exit_status;

      exit_status = WEXITSTATUS (status);
      return exit_status;
    }
}


t_uchar *
make_tmp_tar_archive (t_uchar * dir)
{
  t_uchar * dir_dir = 0;
  t_uchar * dir_tail = 0;
  t_uchar * tmp_stem = 0;
  t_uchar * tmp_in_cwd = 0;
  t_uchar * tmp_path = 0;

  dir_dir = file_name_directory_file (0, dir);
  if (!dir_dir)
    dir_dir = str_save (0, ".");

  dir_tail = file_name_tail (0, dir);

  tmp_stem = str_alloc_cat_many (0, ",,", dir_tail, ".tar.gz", str_end);
  tmp_in_cwd = tmp_file_name (".", tmp_stem);
  tmp_path = file_name_in_vicinity (0, dir_dir, tmp_in_cwd);

  {
    int pid;
    int dev_null_fd;

    dev_null_fd = safe_open ("/dev/null", O_WRONLY, 0);

    pid = fork ();

    if (pid == -1)
      panic ("unable to fork for patch");

    if (!pid)
      {
        t_uchar ** argv;

        safe_chdir (dir_dir);

        argv = 0;

        *(t_uchar **)ar_push ((void **)&argv, 0, sizeof (char *)) = cfg__gnu_tar;
        *(t_uchar **)ar_push ((void **)&argv, 0, sizeof (char *)) = "-zcf";
        *(t_uchar **)ar_push ((void **)&argv, 0, sizeof (char *)) = tmp_in_cwd;
        *(t_uchar **)ar_push ((void **)&argv, 0, sizeof (char *)) = dir_tail;
        *(t_uchar **)ar_push ((void **)&argv, 0, sizeof (char *)) = 0;

        safe_move_fd (dev_null_fd, 1);
        safe_dup2 (1, 2);

        arch_util_execvp (cfg__gnu_tar, argv);
        panic ("make_tmp_tar_archive: execvp for patch returned to caller");
        exit (2);
      }
    else
      {
        int status;
        int wait_pid;

        safe_close (dev_null_fd);

        wait_pid = waitpid (pid, &status, 0);
        if (wait_pid < 0)
          {
            panic_msg ("error waiting for tar subprocess");
            kill (0, SIGKILL);
            panic ("error waiting for subprocess");
          }
        if (WIFSIGNALED (status))
          {
            safe_printfmt (2, "\n");
            safe_printfmt (2, "wait_for_tar: tar subprocess killed by signal %d\n", WTERMSIG (status));
            safe_printfmt (2, "\n");
            exit (2);
            return 0;
          }
        else if (!WIFEXITED (status))
          {
            panic_msg ("waitpid returned for a non-exited process");
            kill (0, SIGKILL);
            panic ("waitpid returned for a non-exited process");
            return 0;
          }
        else
          {
            int exit_status;

            exit_status = WEXITSTATUS (status);

            if (exit_status)
              panic ("make_tmp_tar_archive: tar exitted with non-0 status");
          }
      }
  }

  lim_free (0, dir_dir);
  lim_free (0, dir_tail);
  lim_free (0, tmp_in_cwd);
  lim_free (0, tmp_stem);

  return tmp_path;
}

static void
ensure_writable (struct arch_archive * arch, int mirror_ok)
{
  t_uchar * write_failure;

  write_failure = arch_archive_not_writable (arch, mirror_ok);

  if (write_failure)
   {
     safe_printfmt (2, write_failure);
     exit(2);
   }
}

int
arch_get_meta_int_info(struct arch_archive * arch, t_uchar * key)
{
  t_uchar * key_existence = arch_get_meta_info (arch, key);
  int result  = !!key_existence;

  lim_free (0, key_existence);

  return result;
}


/**
 * Performs a tar on the specified dir, writing output to out_fd
 * out_fd The file descriptor to write to
 * dir The directory to tar up
 */
static void tar_to_fd (int out_fd, t_uchar * dir)
{
  t_uchar * tar_file_path = make_tmp_tar_archive (dir);

  int in_fd = safe_open (tar_file_path, O_RDONLY, 0);
  copy_fd (in_fd, out_fd);
  safe_unlink (tar_file_path);
  safe_close (in_fd);
}


/**
 * If the archive is cached, cache this changeset
 * arch The archive containing the revision
 * revision The revision of the changeset
 * changeset_path The path to the changeset directory
 */
extern void arch_maybe_cache_commit (struct arch_archive *arch, 
                                     t_uchar * revision,
                                     t_uchar * anc_archive,
                                     t_uchar * anc_revision,
                                     t_uchar * changeset_path)
{
  if (str_cmp(arch->vtable->type, "cache") != 0)
    return;
  else
    {
      t_uchar * query = arch_revision_query (arch, revision, "delta.tar.gz");
      t_uchar * tmp_name = 0;
      int put_fd = arch_cache_put (&tmp_name, query);
      t_uchar * fq_ancestor = arch_fully_qualify (anc_archive, anc_revision);
      tar_to_fd (put_fd, changeset_path);
      safe_close (put_fd);
      arch_cache_commit (tmp_name, query);
      
      lim_free (0, query);
      lim_free (0, tmp_name);

      query = arch_revision_query (arch, revision, "ancestor");
      arch_cache_put_line (query, fq_ancestor);
      lim_free (0, query);
      lim_free (0, fq_ancestor);
    }
}

t_uchar *
arch_fs_archive_category_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * category)
{
  if (arch->type == arch_archive_baz)
      panic ("baz does not support stand alone categories");
  return file_name_in_vicinity (0, archive_path, category);
}

t_uchar *
arch_fs_archive_branch_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * package)
{
  t_uchar * category = 0;
  t_uchar * category_path = 0;
  t_uchar * branch_path = 0;

  if (arch->type == arch_archive_baz)
      panic ("baz does not support stand alone branches");

  invariant (arch_valid_package_name (package, arch_no_archive, arch_req_package, 0));

  category = arch_parse_package_name (arch_ret_category, 0, package);
  category_path = arch_fs_archive_category_path (arch, archive_path, category);
  branch_path = file_name_in_vicinity (0, category_path, package);

  lim_free (0, category);
  lim_free (0, category_path);
  return branch_path;
}


t_uchar *
arch_fs_archive_version_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * version)
{
  t_uchar * package = 0;
  t_uchar * package_path = 0;
  t_uchar * version_path = 0;

  invariant (arch_valid_package_name (version, arch_no_archive, arch_req_version, 0));
  
  if (arch->type == arch_archive_baz)
      return str_save (0, version);

  package = arch_parse_package_name (arch_ret_package, 0, version);
  package_path = arch_fs_archive_branch_path (arch, archive_path, package);
  version_path = file_name_in_vicinity (0, package_path, version);

  lim_free (0, package);
  lim_free (0, package_path);

  return version_path;
}

t_uchar *
arch_fs_archive_revision_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * revision)
{
  t_uchar * version = 0;
  t_uchar * version_path = 0;
  t_uchar * level = 0;
  t_uchar * revision_path = 0;

  invariant (arch_valid_package_name (revision, arch_no_archive, arch_req_patch_level, 0));

  version = arch_parse_package_name (arch_ret_package_version, 0, revision);
  version_path = arch_fs_archive_version_path (arch, archive_path, version);
  level = arch_parse_package_name (arch_ret_patch_level, 0, revision);
  revision_path = file_name_in_vicinity (0, version_path, level);

  lim_free (0, version);
  lim_free (0, version_path);
  lim_free (0, level);

  return revision_path;
}

t_uchar *
arch_fs_archive_revision_log_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * revision)
{
  t_uchar * revision_path = 0;
  t_uchar * log_path = 0;

  revision_path = arch_fs_archive_revision_path (arch, archive_path, revision);
  log_path = file_name_in_vicinity (0, revision_path, "log");

  lim_free (0, revision_path);

  return log_path;
}

t_uchar *
arch_fs_archive_changeset_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * revision)
{
  t_uchar * revision_path = 0;
  t_uchar * changeset_path;

  revision_path = arch_fs_archive_revision_path (arch, archive_path, revision);
  changeset_path = file_name_in_vicinity (0, revision_path, revision);
  changeset_path = str_realloc_cat (0, changeset_path, ".patches.tar.gz");

  lim_free (0, revision_path);

  return changeset_path;
}

t_uchar *
arch_fs_archive_import_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * revision)
{
  t_uchar * revision_path = 0;
  t_uchar * changeset_path;

  revision_path = arch_fs_archive_revision_path (arch, archive_path, revision);
  changeset_path = file_name_in_vicinity (0, revision_path, revision);
  changeset_path = str_realloc_cat (0, changeset_path, ".src.tar.gz");

  lim_free (0, revision_path);

  return changeset_path;
}

t_uchar *
arch_fs_archive_cached_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * revision)
{
  t_uchar * revision_path = 0;
  t_uchar * changeset_path;

  revision_path = arch_fs_archive_revision_path (arch, archive_path, revision);
  changeset_path = file_name_in_vicinity (0, revision_path, revision);
  changeset_path = str_realloc_cat (0, changeset_path, ".tar.gz");

  lim_free (0, revision_path);

  return changeset_path;
}

static t_uchar *
arch_fs_archive_per_revision_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * revision, t_uchar const *filename)
{
  t_uchar * revision_path = 0;
  t_uchar * checksum_path = 0;

  revision_path = arch_fs_archive_revision_path (arch, archive_path, revision);
  checksum_path = file_name_in_vicinity (0, revision_path, filename);

  lim_free (0, revision_path);

  return checksum_path;
}

t_uchar *
arch_fs_archive_cached_checksum_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * revision)
{
    return arch_fs_archive_per_revision_path (arch, archive_path, revision, "checksum.cacherev");
}

t_uchar *
arch_fs_archive_ancestry_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * revision)
{
    return arch_fs_archive_per_revision_path (arch, archive_path, revision, "ancestry.gz");
}

t_uchar *
arch_fs_archive_ancestry_checksum_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * revision)
{
    return arch_fs_archive_per_revision_path (arch, archive_path, revision, "ancestry.gz.checksum");
}

t_uchar *
arch_fs_archive_continuation_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * revision)
{
  t_uchar * revision_path = 0;
  t_uchar * continuation_path;

  revision_path = arch_fs_archive_revision_path (arch, archive_path, revision);
  continuation_path = file_name_in_vicinity (0, revision_path, "CONTINUATION");

  lim_free (0, revision_path);

  return continuation_path;
}



t_uchar *
arch_fs_archive_revision_lock_unlocked_path (struct arch_archive * arch,
                                   t_uchar * archive_path,
                                             t_uchar * version,
                                             t_uchar * prev_level)
{
  t_uchar * answer = 0;

  invariant (arch_valid_package_name (version, arch_no_archive, arch_req_version, 0));

  if (!prev_level)
    {
      t_uchar * version_path = 0;

      version_path = arch_fs_archive_version_path (arch, archive_path, version);
      answer = file_name_in_vicinity (0, version_path, "++revision-lock");

      lim_free (0, version_path);
    }
  else
    {
      t_uchar * revision = 0;
      t_uchar * revision_path = 0;

      revision = str_alloc_cat_many (0, version, "--", prev_level, str_end);
      revision_path = arch_fs_archive_revision_path (arch, archive_path, revision);

      answer = file_name_in_vicinity (0, revision_path, "++revision-lock");

      lim_free (0, revision);
      lim_free (0, revision_path);
    }

  return answer;
}


t_uchar *
arch_fs_archive_revision_lock_locked_path (struct arch_archive * arch,
                                 t_uchar * archive_path,
                                           t_uchar * version,
                                           t_uchar * prev_level,
                                           t_uchar * arch_user_id,
                                           t_uchar * txn_id)
{
  t_uchar * version_path = 0;
  t_uchar * lock_basename = 0;
  t_uchar * answer = 0;

  invariant (arch_valid_package_name (version, arch_no_archive, arch_req_version, 0));

  version_path = arch_fs_archive_version_path (arch, archive_path, version);

  lock_basename = str_alloc_cat_many (0,
                                      "++revision-lock-held--",
                                      (prev_level ? prev_level : (t_uchar *)"absolute-0"),
                                      "--",
                                      arch_user_id,
                                      (txn_id ? "--" : 0),
                                      txn_id,
                                      str_end);

  answer = file_name_in_vicinity (0, version_path, lock_basename);

  lim_free (0, version_path);
  lim_free (0, lock_basename);
  return answer;
}


t_uchar *
arch_fs_archive_revision_lock_locked_contents_path (struct arch_archive * arch,
                                        t_uchar * archive_path,
                                                    t_uchar * version,
                                                    t_uchar * prev_level,
                                                    t_uchar * arch_user_id,
                                                    t_uchar * txn_id)
{
  t_uchar * locked_path = 0;
  t_uchar * answer = 0;

  locked_path = arch_fs_archive_revision_lock_locked_path (arch, archive_path, version, prev_level, arch_user_id, txn_id);
  answer = file_name_in_vicinity (0, locked_path, "+contents");

  lim_free (0, locked_path);
  return answer;
}


t_uchar *
arch_fs_archive_revision_lock_broken_path (struct arch_archive * arch,
                                 t_uchar * archive_path,
                                           t_uchar * version,
                                           t_uchar * prev_level)
{
  t_uchar * version_path = 0;
  t_uchar * lock_dir_basename = 0;
  t_uchar * lock_basename = 0;
  t_uchar * broken_dir = 0;
  t_uchar * answer = 0;

  version_path = arch_fs_archive_version_path (arch, archive_path, version);

  lock_dir_basename = str_alloc_cat (0, "++revision-lock-broken--", (prev_level ? prev_level : (t_uchar *)"absolute-0"));
  lock_basename = str_alloc_cat (0, ",,remade-lock--", (prev_level ? prev_level : (t_uchar *)"absolute-0"));

  broken_dir = file_name_in_vicinity (0, version_path, lock_dir_basename);
  answer = file_name_in_vicinity (0, broken_dir, lock_basename);

  lim_free (0, version_path);
  lim_free (0, lock_dir_basename);
  lim_free (0, lock_basename);
  lim_free (0, broken_dir);
  return answer;
}






/* tag: Tom Lord Tue May 20 00:52:06 2003 (archive.c)
 */

Generated by  Doxygen 1.6.0   Back to index