/* patch-id.c:
 *
 * vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2
 ****************************************************************
 * 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/bugs/exception.h"
#include "hackerlab/char/str.h"
#include "hackerlab/sort/qsort.h"
#include "libarch/namespace.h"
#include "libarch/patch-id.h"



struct sort_spec
{
  int reverse;
};

static int patch_id_cmp (void *va,
                         void *vb,
                         void *vdata);
static int arch_patch_id_destructor (void * data);



void
arch_patch_id_init (arch_patch_id *patch, t_uchar const *patch_id)
{
    /* cannot use talloc onto the patch while this is
     * allowed on the stack
     */
    patch->patch_id = talloc_strdup (NULL, patch_id);
    patch->archive = NULL;
    patch->branch = NULL;
    patch->version = NULL;
    patch->revision = NULL;
    patch->patchlevel = NULL;
}

void
arch_patch_id_init_archive (arch_patch_id *patch, t_uchar const *archive, t_uchar const *revision)
{
    t_uchar *fqrevision = str_alloc_cat_many (0, archive, "/", revision, str_end);
    arch_patch_id_init (patch, fqrevision);
    lim_free (0, fqrevision);
}

/**
 * \brief create a new patch with the id patch_id
 * \param patch_id the patch id
 * \return the patch, freshly allocated
 */
arch_patch_id *
arch_patch_id_new (t_uchar const * patch_id)
{
  arch_patch_id * answer = 0;

  answer = talloc (NULL, arch_patch_id);
  talloc_set_destructor (answer, arch_patch_id_destructor);
  arch_patch_id_init (answer, patch_id);

  return answer;
}
/**
 * \brief Create a copy of the given patch.
 *
 * This is mainly used in the same way str_save is used.
 */
arch_patch_id *
arch_patch_id_copy (arch_patch_id *patch)
{
  return arch_patch_id_new (arch_patch_id_patch_id (patch));
}

/**
 * \brief Create a new arch_patch_id initialized by the supplied archive and revision.
 */
arch_patch_id *
arch_patch_id_new_archive (t_uchar const *archive,
                              t_uchar const *revision)
{
  arch_patch_id * answer = 0;

  answer = talloc (NULL, arch_patch_id);
  talloc_set_destructor (answer, arch_patch_id_destructor);
  arch_patch_id_init_archive (answer, archive, revision);

  return answer;
}

/**
 * \brief Free the memory for the contained members, but not the object itself.
 */
void 
arch_patch_id_finalise (arch_patch_id *patch)
{
    talloc_free (patch->patch_id);
    patch->patch_id = NULL;
    lim_free (0, patch->archive);
    patch->archive = NULL;
    lim_free (0, patch->branch);
    patch->branch = NULL;
    lim_free (0, patch->version);
    patch->version = NULL;
    lim_free (0, patch->revision);
    patch->revision = NULL;
    lim_free (0, patch->patchlevel);
    patch->patchlevel = NULL;
}

/**
 * \brief Get the archive portion of the patch_id.
 */
t_uchar *
arch_patch_id_archive (arch_patch_id *patch)
{
    if (!patch->archive)
        patch->archive = arch_parse_package_name (arch_ret_archive, 0, patch->patch_id);
    return patch->archive;
}

/**
 * \brief Get the fully qualified version (where patches go).
 */
t_uchar *
arch_patch_id_branch (arch_patch_id *patch)
{
    if (!patch->branch)
        patch->branch = arch_fully_qualify (arch_patch_id_archive(patch), arch_patch_id_version (patch));
    return patch->branch;
}

t_uchar *
arch_patch_id_version (arch_patch_id *patch)
{
    if (!patch->version)
        patch->version = arch_parse_package_name (arch_ret_package_version, 0, patch->patch_id);
    return patch->version;
}

t_uchar *
arch_patch_id_revision (arch_patch_id *patch)
{
    if (!patch->revision)
        patch->revision = arch_parse_package_name (arch_ret_non_archive, 0, patch->patch_id);
    return patch->revision;
}

t_uchar *
arch_patch_id_patch_id (arch_patch_id *patch)
{
    return patch->patch_id;
}

t_uchar *
arch_patch_id_patchlevel (arch_patch_id *patch)
{
    if (!patch->patchlevel)
        patch->patchlevel = arch_parse_package_name (arch_ret_patch_level, 0, patch->patch_id);
    return patch->patchlevel;
}

/**
 * \brief Remove the arch_patch_ids from other_patches and put them in patches
 *
 * \param patches All patches will end up here
 * \param other_patches This should end up empty.
 */
void 
ar_patch_id_extend_taking (ar_patch_id * patches,
                           ar_patch_id * other_patches)
{
  while (ar_size_patch_id (*other_patches) > 0)
    {
      ar_push_patch_id (patches, ar_pop_patch_id (other_patches));
      talloc_steal (ar_base (*patches), (*patches)[ar_size_patch_id (*patches) - 1]);
    }
  ar_free_patch_id (other_patches);
}

/**
 * \brief Compare two patch_ids using the individual components.
 */
int 
arch_patch_id_cmp (arch_patch_id *a,
                   arch_patch_id *b)
{
  return arch_names_cmp (arch_patch_id_patch_id (a), arch_patch_id_patch_id (b));
}

/**
 * \brief Sort a list of arch_patch_ids base on their components.
 *
 * \sa arch_sort_table_by_name_field
 */
void 
ar_sort_patch_id (ar_patch_id patches,
                  int reverse)
{
  struct sort_spec spec = {reverse};

  quicksort ((void *)patches, ar_size_patch_id (patches),
      sizeof (arch_patch_id *), patch_id_cmp, (void *)&spec);
}



int 
patch_id_cmp (void *va,
              void *vb,
              void *vdata)
{
  arch_patch_id *a = 0;
  arch_patch_id *b = 0;
  struct sort_spec * spec = 0;

  spec = (struct sort_spec *) vdata;
  a = (arch_patch_id*) va;
  b = (arch_patch_id*) vb;

  if (spec->reverse)
    {
      return -arch_patch_id_cmp (a, b);
    }
  else
    {
      return arch_patch_id_cmp (a, b);
    }
}


/**
 * \brief free resources for patch ids. Until all the static instances are
 * talloced, this must be assigned to every talloced instance
 */
int
arch_patch_id_destructor (void * data)
{
    arch_patch_id * patch = talloc_get_type (data, arch_patch_id);
    invariant (!!patch);
    arch_patch_id_finalise (patch);
    return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1