Logo Search packages:      
Sourcecode: bazaar version File versions

ancestry.c

/* ancestry.c:
 *
 * vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 * Copyright (C) 2005 Canonical Limited
 *        Authors: Robert Collins <robert.collins@canonical.com>
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/bugs/panic.h"
#include "hackerlab/arrays/ar.h"
#include "hackerlab/char/str.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/os/sys/wait.h"
#include "hackerlab/vu/safe.h"
#include "libawk/associative.h"
#include "libawk/trim.h"
#include "libfsutils/file-contents.h"
#include "libfsutils/tmp-files.h"
#include "libarch/archives.h"
#include "libarch/arch-cache.h"
#include "libarch/cached-archive.h"
#include "libarch/libraries.h"
#include "libarch/namespace.h"
#include "libarch/merge-points.h"
#include "libarch/patch-id.h"
#include "libarch/ancestry.h"


/* TODO this belongs as the underlying mechanism for arch_archive_connect.. name, 0, ?, ? */
typedef struct _arch_archive_connection_cache
{
    struct arch_archive **cache;
} arch_archive_connection_cache;

static void arch_archive_connection_cache_init (arch_archive_connection_cache * cache);
static void arch_archive_connection_cache_finalise (arch_archive_connection_cache * cache, struct arch_archive * dontclose);
static struct arch_archive * arch_archive_connection_cache_find_or_maybe_connect (arch_archive_connection_cache * cache, t_uchar * name, int soft_errors);
static void arch_archive_connection_cache_add (arch_archive_connection_cache * cache, struct arch_archive * arch);
static t_uchar * arch_ancestry_from_revision (t_uchar const * patch_id, int soft_errors);
static void arch_ancestry_note_ancestor (t_uchar const *patch_id, t_uchar const *ancestor);



rel_table
arch_trace_ancestry (struct arch_archive * arch, t_uchar * archive, t_uchar * revision, int merges)
{
  rel_table answer = 0;
  int done = 0;
  arch_archive_connection_cache cache;

  arch_archive_connection_cache_init (&cache);
  archive = str_save (0, archive);
  revision = str_save (0, revision);

  if (arch)
      arch_archive_connection_cache_add (&cache, arch);

  while (!done)
    {
      t_uchar * fq_rev = 0;
      struct arch_archive * this_arch = 0;

      fq_rev = arch_fully_qualify (archive, revision);
      this_arch = arch_archive_connection_cache_find_or_maybe_connect (&cache, archive, 0);

      if (!this_arch)
        {
          rel_add_records (&answer, rel_make_record (fq_rev, "???", 0), 0);
          done = 1;
        }
      else
        {
          t_uchar * ancestor = 0;

          if (!merges)
            rel_add_records (&answer, rel_make_record (fq_rev, fq_rev, 0), 0);
          else
            {
              enum arch_revision_type type;

              /* FIXME: grab ancestry here if it exists */
              arch_revision_type (&type, 0, NULL, this_arch, revision);

              if (type == arch_continuation_revision)
                {
                  rel_add_records (&answer, rel_make_record (fq_rev, fq_rev, 0), 0);
                }
              else
                {
                  rel_table merges = 0;
                  int x;

                  merges = arch_archive_merge_points (this_arch, revision, 0, 0, 0);
                  for (x = 0; x < rel_n_records (merges); ++x)
                    {
                      rel_add_records (&answer, rel_make_record (fq_rev, merges[x][1], 0), 0);
                    }
                  rel_free_table (merges);
                }
            }

          ancestor = arch_patch_ancestor (fq_rev, 0, 0);

          if (!ancestor)
            {
              done = 1;
            }
          else
            {
              lim_free (0, archive);
              lim_free (0, revision);

              archive = arch_parse_package_name (arch_ret_archive, 0, ancestor);
              revision = arch_parse_package_name (arch_ret_non_archive, 0, ancestor);
            }

          lim_free (0, ancestor);
        }

      lim_free (0, fq_rev);
    }
  
  arch_archive_connection_cache_finalise (&cache, arch);

  lim_free (0, archive);
  lim_free (0, revision);
  return answer;
}



void
arch_archive_connection_cache_init (arch_archive_connection_cache * cache)
{
    cache->cache = NULL;
}

void 
arch_archive_connection_cache_finalise (arch_archive_connection_cache * cache, struct arch_archive * dontclose)
{
    int position;

    for (position = 0; position < ar_size ((void *)cache->cache, 0, sizeof (struct arch_archive *)); ++position)
      {
        if (cache->cache[position] != dontclose)
          arch_archive_close (cache->cache[position]);
      }
    ar_free ((void **)&(cache->cache), 0);
    cache->cache = NULL;
}

static struct arch_archive *
arch_archive_connection_cache_find_or_maybe_connect (arch_archive_connection_cache * cache, t_uchar *name, int soft_errors)
{
  int position;
  t_uchar * loc = 0;
  struct arch_archive * arch = 0;

  for (position = 0; position < ar_size (cache->cache, 0, sizeof (struct arch_archive *)); ++position)
    {
      if (!str_cmp (name, cache->cache[position]->name))
        return (cache->cache)[position];
    }

  loc = arch_archive_location (name, 1);

  if (loc)
    {
      arch = arch_archive_connect_ext (name, 0, soft_errors);
      invariant (!arch || !str_cmp (name, arch->official_name));
      if (arch)
          arch_archive_connection_cache_add (cache, arch);
    }

  lim_free (0, loc);
  return arch;
}

void
arch_archive_connection_cache_add (arch_archive_connection_cache * cache, struct arch_archive * arch)
{
  *(struct arch_archive **)ar_push ((void **)&(cache->cache), 0, sizeof (struct arch_archive *)) = arch;
}

static int
ancestry_parse_baz_1_is_patch_id (t_uchar const *record)
{
    if (!str_cmp_prefix (ARCH_ANCESTRY_PATCH_PREFIX, record ))
        return -1;
    if (!str_cmp (ARCH_ANCESTRY_PATCH_PARTIAL, record ))
        return -1;
    return 0;
}

static t_uchar *
ancestry_parse_baz_1_patch_id (t_uchar const *record)
{
    if (!str_cmp_prefix (ARCH_ANCESTRY_PATCH_PREFIX, record ))
        return str_save (0, record + str_length (ARCH_ANCESTRY_PATCH_PREFIX));
    if (!str_cmp (ARCH_ANCESTRY_PATCH_PARTIAL, record ))
        return str_save (0, record);
    return NULL;
}

static rel_table
ancestry_parse_baz_1 (rel_table raw, int start_from)
{
    rel_table result = NULL;
    int current_line;
    for (current_line = start_from; current_line < rel_n_records(raw); ++current_line)
      {
        if (ancestry_parse_baz_1_is_patch_id (raw[current_line][0]))
          {
            t_uchar *patch_id = ancestry_parse_baz_1_patch_id (raw[current_line][0]);
            rel_add_records(&result, rel_make_record (patch_id, 0), 0);
            lim_free (0, patch_id);
          }
      }
    return result;
}

static rel_table
ancestry_parse(t_uchar const *content)
{
    /* TODO: if it looks like its gpg, run it through gpg */
    rel_table temp_table = rel_nl_split ((t_uchar *)content);
    rel_table answer = NULL;
    int start_record;

    for (start_record = 0; start_record < rel_n_records (temp_table); ++start_record)
        if (!str_cmp(ARCH_ANCESTRY_VERSION_BAZ_1, temp_table[start_record][0]))
          {
            answer = ancestry_parse_baz_1 (temp_table, start_record + 1);
            break;
          }
    rel_free_table (temp_table);
    return answer;
}

static rel_table
partial_ancestry_from_full(rel_table full, t_uchar const *patch_id)
{
    rel_table result = NULL;
    int row = 0;
    while (row < rel_n_records (full) && str_cmp (patch_id, full[row][0]))
           ++row;
    for (; row < rel_n_records (full); ++row)
      {
        rel_add_records (&result, rel_copy_record (full[row]), 0);
      }
    return result;
}

static assoc_table arch_ancestors = NULL;
static arch_archive_connection_cache global_cache = { NULL };

static struct arch_archive *
cached_archive_connection (t_uchar const *patch_id, int soft_errors)
{
    t_uchar * archive = arch_parse_package_name(arch_ret_archive, 0, patch_id);
    struct arch_archive *arch = NULL;
    t_uchar * location;
    location = arch_archive_location (archive, soft_errors);
    if (location)
        arch = arch_archive_connection_cache_find_or_maybe_connect (&global_cache, archive, soft_errors);
    lim_free (0, archive);
    lim_free (0, location);
    return arch;
}

/* when the archive caches moves to archive.c, this moves to 
 * cached_archive.c
 */
static t_uchar *
arch_patch_id_query (t_uchar const * patch_id, t_uchar const *key)
{
  arch_patch_id patch; 
  t_uchar *result = NULL;
  
  arch_patch_id_init (&patch, patch_id); 
  
  result = arch_cache_query_new (&patch, key);
  
  arch_patch_id_finalise (&patch);
  return result;
}

/* returns:
 * PARTIAL for no known ancestory
 * NULL for no ancestor
 * other string for a specific ancestor.
 */
static t_uchar *
arch_cached_patch_ancestor (t_uchar const * patch_id)
{
    t_uchar *result = assoc_ref (arch_ancestors, (t_uchar *)patch_id);
    invariant (str_cmp (patch_id, ARCH_ANCESTRY_PATCH_PARTIAL));
    if (result && str_cmp (result, ARCH_ANCESTRY_PATCH_PARTIAL))
      {
        /* penultimate ancestor */
        if (!str_length (result))
          {
            return NULL;
          }
        return str_save(0, result);
      }
    /* memory cache miss */
      {
        t_uchar * ancestor_query = arch_patch_id_query (patch_id, "ancestor");
        if (ancestor_query && arch_cache_has_answer (ancestor_query))
          {
            result = arch_cache_get_line (ancestor_query);
            invariant (result != NULL);
            result = trim_surrounding_ws (result);
          }
        lim_free (0, ancestor_query);
        if (result)
          {
            /* penultimate ancestor */
            if (!str_length (result))
              {
                return NULL;
              }
            return str_save(0, result);
          }
      }
    /* not there either, return partial */
    return str_save (0, ARCH_ANCESTRY_PATCH_PARTIAL);
}

/* refactored arch_ancestor_revision
 * will:
 * exit on failure unless soft_errors is set.
 * return PARTIAL on failures otherwise.
 * return the ancestor patch id, and 
 * return NULL for the terminal
 * ancestor.
 *
 * find the ancestor for patch_id from:
 *   - known ancestry data
 *   - the archive
 *
 * maintains the in-core ancestry cache.
 */

t_uchar *
arch_patch_ancestor (t_uchar const * patch_id, int soft_errors, int cached_only)
{
    /* cache lookup */
    t_uchar *result = arch_cached_patch_ancestor (patch_id);
    if (!result || (str_cmp (result, ARCH_ANCESTRY_PATCH_PARTIAL) ))
        /* penultimate ancestor or known ancestor */
        return result;
    lim_free (0, result);
    result = NULL;
    /* cache miss */
    if (!cached_only)
      {
        result = arch_ancestry_from_revision (patch_id, soft_errors);
        if (!result || (str_cmp (result, ARCH_ANCESTRY_PATCH_PARTIAL)))
            /* penultimate ancestor or known ancestor */
            return result;
        lim_free (0, result);
        result = NULL;
        /* old api .. */
        struct arch_archive *arch = cached_archive_connection (patch_id, soft_errors);

        if (arch)
          {
            t_uchar * revision = arch_parse_package_name(arch_ret_non_archive, 0, patch_id);
            /* FIXME: teach arch_ancestor_revision about soft errors */
            result = arch_ancestor_revision (arch, revision);
            lim_free (0, revision);
            if (!result) 
              {
                /* penultimate ancestor */
                arch_ancestry_note_ancestor (patch_id, "");
                return NULL;
              }
            else /* when ancestor_revision does soft errors..
                    if (str_cmp (result, ARCH_ANCESTRY_PATCH_PARTIAL)) */
              {
                arch_ancestry_note_ancestor (patch_id, result);
                return result;
              }
          }
        
      }
    /* no cache hit, and couldn't get anything from the archive */
    if (soft_errors)
        return str_save (0, ARCH_ANCESTRY_PATCH_PARTIAL);
    else
        exit(2);
}

/* we've observed the ancestor patch_id:
 * "" for patch_id is the import rev.
 * ARCH_ANCESTRY_PATCH_PARTIAL for we are noting that we couldn't find the ancestor
 * $other_patch_id for a specific ancestor
 */
void
arch_ancestry_note_ancestor (t_uchar const *patch_id, t_uchar const *ancestor)
{
    t_uchar *current = arch_cached_patch_ancestor (patch_id);
    if (current && !str_cmp(current, ARCH_ANCESTRY_PATCH_PARTIAL))
      {
        invariant (ancestor != NULL);
        invariant (str_cmp (patch_id, ARCH_ANCESTRY_PATCH_PARTIAL));
        t_uchar * ancestor_query = arch_patch_id_query (patch_id, "ancestor");
        arch_cache_put_line (ancestor_query, ancestor);
        assoc_set (&arch_ancestors, (t_uchar *)patch_id, (t_uchar *)ancestor);
        lim_free (0, ancestor_query);
      }
    lim_free (0, current);
}

static void
arch_ancestry_insert_ancestry (rel_table revisions)
{
    int row;
    if (!revisions)
        return;
    for (row=0; row < rel_n_records (revisions) - 1; ++row)
        arch_ancestry_note_ancestor (revisions[row][0], revisions[row + 1][0]);
    if (str_cmp(ARCH_ANCESTRY_PATCH_PARTIAL, revisions[rel_n_records (revisions) - 1][0]))
        arch_ancestry_note_ancestor (revisions[rel_n_records (revisions) - 1][0], "");
}

/* append locally available ancestry data 
 * precondition: there is at least one non-PARTIAL row in current_ancestry
 */
static rel_table
ancestry_append_available (rel_table current_ancestry)
{
    rel_table answer = NULL;
    t_uchar * fq_current;
    int position = 0;
    if (!rel_n_records(current_ancestry))
        return NULL;
    fq_current = current_ancestry[position][0];
    while (fq_current && str_cmp (ARCH_ANCESTRY_PATCH_PARTIAL, fq_current))
      {
        rel_add_records (&answer, rel_copy_record (current_ancestry[position]), 0);
        ++position;
        if (rel_n_records (current_ancestry) > position)
            fq_current = current_ancestry[position][0];
        else
            fq_current = NULL;
      }
    
    fq_current = str_save (0, answer[rel_n_records(answer) - 1][0]); 
    while (fq_current && str_cmp (fq_current, ARCH_ANCESTRY_PATCH_PARTIAL))
      {
        t_uchar *next = arch_patch_ancestor (fq_current, 1,1);
        if (next)
            rel_add_records (&answer, rel_make_record (next, 0), 0);
        lim_free (0, fq_current);
        fq_current = next;
      }
    lim_free (0, fq_current);
    return answer;

}

/* scan the network for the ancestry relating to patch_id.
 * stop when minimum_depth is reached, or if we join with
 * maybe_ancestor.
 */
static rel_table
ancestry_scan (t_uchar const * patch_id, rel_table maybe_ancestor, int minimum_depth, int soft_errors)
{
  t_uchar * fq_current = str_save (0, patch_id);
  rel_table answer = NULL;
  rel_table final = NULL;
  int depth = 0;

  arch_ancestry_insert_ancestry (maybe_ancestor);
  
  rel_add_records (&answer, rel_make_record ((t_uchar *)patch_id, 0), 0);
  
  while (minimum_depth == -1 || depth < minimum_depth)
    {
      /* pre-condition:
       *
       * fq_current tell use the patch_id to append to the result.
       */
      
      int partial = 0;
      t_uchar * fq_next_revision = 0;
      
      fq_next_revision = arch_patch_ancestor (fq_current, 1, 0);
      if (!fq_next_revision)
          /* end of ancestry */
        {
          lim_free (0, fq_next_revision);
          fq_next_revision = NULL;
        }
      if (fq_next_revision && !str_cmp (ARCH_ANCESTRY_PATCH_PARTIAL, fq_next_revision))
          /* couldn't search further */
        {
          lim_free (0, fq_next_revision);
          rel_add_records(&answer, rel_make_record (ARCH_ANCESTRY_PATCH_PARTIAL, 0), 0);
          fq_next_revision = NULL; 
          partial = -1;
        }
      lim_free (0, fq_current);
      if (fq_next_revision)
          /* normal ancestor */
        {
          rel_add_records (&answer, rel_make_record (fq_next_revision, 0), 0);
          fq_current = str_save (0, fq_next_revision);
        }
      else
          fq_current = NULL;
      lim_free (0, fq_next_revision);
      ++depth;
      
      if (!fq_current || partial)
          break;
    }
  lim_free (0, fq_current);
  final = ancestry_append_available (answer);
  rel_free_table (answer);
  arch_archive_connection_cache_finalise (&global_cache, NULL);
  return final;
}
  
static void
ancestry_snap (struct arch_project_tree *tree, rel_table answer)
{
    int ignore;
    int outfd;
    t_uchar * ancestry_file = file_name_in_vicinity (0, tree->root, ARCH_PROJECT_TREE_ANCESTRY_FILE);
    
    vu_unlink (&ignore, ancestry_file);
    outfd = safe_open (ancestry_file, (O_WRONLY | O_CREAT | O_EXCL), 0444);
    lim_free (0, ancestry_file);

    ancestry_write (answer, outfd, -1);
    safe_close (outfd);
}

void
ancestry_write (rel_table answer, int outfd, int maximum_depth)
{
    int position;
    if (maximum_depth < 1 || maximum_depth > rel_n_records(answer))
      {
        maximum_depth = rel_n_records (answer);
      }
    safe_printfmt (outfd, ARCH_ANCESTRY_VERSION_BAZ_1"\n");
    for (position = 0; position < maximum_depth ; ++position)
      {
        if (str_cmp (ARCH_ANCESTRY_PATCH_PARTIAL, answer[position][0]))
            safe_printfmt (outfd, ARCH_ANCESTRY_PATCH_PREFIX"%s\n", answer[position][0]);
        else
            safe_printfmt (outfd, "%s\n", answer[position][0]);
      }
    if (maximum_depth < rel_n_records (answer))
        safe_printfmt (outfd, "%s\n", ARCH_ANCESTRY_PATCH_PARTIAL);
}

/* retrieve the patch ancestry for for patch_id from tree tree */
static rel_table
patch_ancestry_from_tree (struct arch_project_tree *tree, t_uchar * patch_id, int minimum_depth, int soft_errors)
{
    rel_table answer = NULL;
    if (arch_project_tree_file_exists(tree, ARCH_PROJECT_TREE_ANCESTRY_FILE))
      {
        t_uchar *candidate_text = arch_project_tree_file_contents(tree, ARCH_PROJECT_TREE_ANCESTRY_FILE);
        rel_table candidate = ancestry_parse(candidate_text);
        lim_free (0, candidate_text);
        
        answer = partial_ancestry_from_full (candidate, patch_id);

        rel_free_table (candidate);
        
      }
    return answer;
}

/* patch_ancestry:
 * retrieve the ancestry for the patch patch_id, looking for 
 * a minimum depth as supplied. If there are more revisions
 * than minimum_depth trivially available, they will be supplied 
 * to the caller. -1 means retrieve the entire ancestry.
 * 0 means retrieve as much as is immediately available.
 *
 * The ancestry is returned as a rel_table. it has one field
 * the patch-id for that element of the ancestry. The most
 * recent revisions are at the lowest entries in the
 * returned table.
 *
 * Once the ancestry is calculated, it is automatically
 * memoised to {arch}/+ancestry. 
 * TODO: teach commit to update +ancestry in the archive
 * upon commit
 * 
 * See http://wiki.gnuarch.org/moin.cgi/AncestryFileFormat
 * for the general format of these files.
 *
 * If soft_errors is 1, any failure encountered will result in 
 * NULL for the result
 * If it is 2, failures will result in the current known history
 * being returned.
 *
 * history is established by the following lookup priority:
 * 1) If there is a {arch}/+ancestry file it is processed.
 * 
 * 2) If there is a pristine tree available for patch_id,
 *    it is consulted for a +ancestry file.
 *    
 * 3) The file ancestry is looked for in the archive that the
 *    patch belongs too - if it has such a archive, and its
 *    registered.
 *
 * 4) The log message for the request revision is consulted
 *    for its immediate ancestor, and then the search resumes 
 *    from there.
 */

rel_table
patch_ancestry (struct arch_project_tree *optional_tree, t_uchar const * patch_id, int minimum_depth, int soft_errors)
{
    rel_table answer = NULL;
    /* look in the local tree */
    if (optional_tree && arch_project_tree_file_exists(optional_tree, ARCH_PROJECT_TREE_ANCESTRY_FILE))
      {
        int do_snap = 0;
        t_uchar *candidate_text = arch_project_tree_file_contents(optional_tree, ARCH_PROJECT_TREE_ANCESTRY_FILE);
        rel_table candidate = ancestry_parse(candidate_text);
        lim_free (0, candidate_text);
        
        answer = partial_ancestry_from_full (candidate, patch_id);
        if (!answer)
          {
            answer = ancestry_scan (patch_id, candidate, minimum_depth, soft_errors);
            do_snap = -1;
          }

        rel_free_table (candidate);
        
        if (!answer)
            
          {
            if (soft_errors)
                return NULL;
            else
              {
                safe_printfmt (2, "Empty or invalid cached ancestry file\n");
                exit (2);
              }
          }
        if (do_snap)
            ancestry_snap (optional_tree, answer);
        return answer;
      }
    /* look in a reference tree */
      {
        t_uchar *archive = arch_parse_package_name (arch_ret_archive, 0, patch_id);
        t_uchar *revision = arch_parse_package_name (arch_ret_non_archive, 0, patch_id);
        t_uchar *pristine = arch_library_find (NULL, archive, revision, 1);
        struct arch_project_tree pristine_tree;
        
        if (pristine)
          {
            arch_project_tree_init (&pristine_tree, pristine);
            answer = patch_ancestry_from_tree (&pristine_tree, (t_uchar *)patch_id, minimum_depth, soft_errors);
            arch_project_tree_finalise(&pristine_tree);
          }

        lim_free (0, pristine);
        lim_free (0, archive);
        lim_free (0, revision);
      }
    if (answer)
        goto finished;
    /* look for a cached ancestry file in the revision */
    /* answer = ... */

    /* Check we got enough */
    if (minimum_depth > 0 && answer && 
        !str_cmp (answer[rel_n_records(answer) - 1][0], ARCH_ANCESTRY_PATCH_PARTIAL) &&
        rel_n_records(answer) < minimum_depth)
      {
        /* TODO finish the min depth stuff
         * recursion isn't the best, may make sense to turn these few calls
         * into a loop...
         *
        int minimum = minimum_depth - rel_n_records(answer) ;
        rel_table temp = patch_ancestry (NULL, answer[rel_n_records(answer) - 2][0], remaining, soft_errors);
        remove the PARTIAL record and append the result we got to answer.
        goto finished;
        */
      }
    
    /* scan the network */
    answer = ancestry_scan (patch_id, NULL, minimum_depth, soft_errors);
    /* TODO: assert answer ? */
finished:
    if (optional_tree && answer)
        ancestry_snap (optional_tree, answer);
    return answer;
}

/* insert a ancestry file to a revision 
 * -1 depth means all known,
 *  0 means just the revisions data*/
void
ancestry_upload_patch (struct arch_archive * arch, t_uchar const *fqrevision, int max_depth)
{
    int infd = -1;
    /* working file */
    t_uchar *tmp_file = tmp_file_name (".", ",,ancestry");
    t_uchar *tmp_gz_file = tmp_file_name (".", ",,ancestry");
    /* generate ancestry data */
    rel_table ancestry = patch_ancestry (NULL, fqrevision, max_depth, 0);
    invariant (rel_n_records (ancestry) > 0);
    /* create a ancestry file */
      {
        int outfd = safe_open (tmp_file, (O_WRONLY | O_CREAT | O_EXCL), 0444);
        ancestry_write (ancestry, outfd, max_depth);
        safe_close (outfd);
      }
        
    /* gzip it */
    /* FIXME: magic etc etc config detection for gzip */
      {
        /* FIXME: write a system replacement using ar */
        t_uchar * command = str_alloc_cat_many (0, "gzip -c '", tmp_file, "' > '", tmp_gz_file, "'", str_end);
        int status = system (command);
        if (status == -1)
          {
            safe_printfmt (2, "Fork failed when running gzip\n");
            exit (2);
          }
        if (!WIFEXITED (status))
          {
            safe_printfmt (2, "Child process did not exit - caught a signal or something unexpected happened, status is (%d)\n", status);
            exit (2);
          }
        if (WEXITSTATUS (status))
          {
            safe_printfmt (2, "gzipping ancestry file for upload to the archive failed, status is (%d)\n", WEXITSTATUS(status));
            exit (2);
          }
      }
    /* unlink the old file */
    safe_unlink (tmp_file);
    /* open the zip */
    infd = safe_open (tmp_gz_file, O_RDONLY, 0);
    
    /* upload */
      {
        t_uchar *error = NULL;
        t_uchar *revision = arch_parse_package_name (arch_ret_non_archive, 0, fqrevision);
        arch_archive_put_ancestry (&error, arch, revision, infd);
        lim_free (0, revision);
      }
    
    safe_close (infd);
    safe_unlink (tmp_gz_file);
    lim_free (0, tmp_file);
    lim_free (0, tmp_gz_file);
}

/* insert/update an ancestry file for a branch
 * -1 depth means all known,
 *  0 means just the revisions data
 */

void
ancestry_upload_branch (struct arch_archive * arch, t_uchar const *fqrevision, int max_depth)
{
    /* generate ancestry data */
    rel_table ancestry = patch_ancestry (NULL, fqrevision, max_depth, 0);
    invariant (rel_n_records (ancestry) > 0);
}

t_uchar *
arch_ancestry_from_revision (t_uchar const * patch_id, int soft_errors)
{
    /* retrieve tarball */
    struct arch_archive *arch = cached_archive_connection (patch_id, soft_errors);
    t_uchar *tmp_gz_file = tmp_file_name (".", ",,ancestry");
    t_uchar *tmp_file = tmp_file_name (".", ",,ancestry");
    
    
    if (arch)
      {
        int ignore;
        int outfd;
        t_uchar * revision = arch_parse_package_name(arch_ret_non_archive, 0, patch_id);
        int has_ancestry;
        
        arch_revision_type (NULL, NULL, &has_ancestry, arch, revision);
        if (!has_ancestry)
          {
            lim_free (0, revision);
            goto finished;
          }

        vu_unlink (&ignore, tmp_gz_file);
        outfd = safe_open (tmp_gz_file, (O_WRONLY | O_CREAT | O_EXCL), 0444);

        arch_archive_get_ancestry (outfd, arch, revision);
        safe_close (outfd);
        lim_free (0, revision);
      }
    else
      goto finished;

    /* ungzip it */
    /* FIXME: magic etc etc config detection for gzip */
      {
        /* FIXME: write a system replacement using ar and pipe the result */
        t_uchar * command = str_alloc_cat_many (0, "gunzip -c '", tmp_gz_file, "' > '", tmp_file, "'", str_end);
        int status = system (command);
        if (status == -1)
          {
            safe_printfmt (2, "Fork failed when running gunzip\n");
            exit (2);
          }
        if (!WIFEXITED (status))
          {
            safe_printfmt (2, "Child process did not exit - caught a signal or something unexpected happened, status is (%d)\n", status);
            exit (2);
          }
        if (WEXITSTATUS (status))
          {
            safe_printfmt (2, "un gzipping ancestry file from archive failed, status is (%d)\n", WEXITSTATUS(status));
            exit (2);
          }
      }

    /* unlink the old file */
    safe_unlink (tmp_gz_file);
    
      {
        t_uchar *ancestry_text = file_contents(tmp_file);
        rel_table ancestry = ancestry_parse(ancestry_text);
        arch_ancestry_insert_ancestry (ancestry);
        rel_free_table (ancestry);
        lim_free (0, ancestry_text);
      }

    safe_unlink (tmp_file);

finished:
    lim_free (0, tmp_file);
    lim_free (0, tmp_gz_file);

    return arch_cached_patch_ancestor (patch_id);
}

/* tag: Tom Lord Thu Jun 26 18:41:53 2003 (ancestry.c)
 */

Generated by  Doxygen 1.6.0   Back to index