/* namespace.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 "hackerlab/bugs/panic.h"
#include "hackerlab/fmt/cvt.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/char/str.h"
#include "hackerlab/sort/qsort.h"
#include "hackerlab/vu/safe.h"
#include "libfsutils/safety.h"
#include "libarch/my.h"
#include "libarch/namespace.h"


/* __STDC__ prototypes for static functions */
static int version_cmp_n (const t_uchar * a, size_t a_l, const t_uchar * b, size_t b_l);
static int ver_order (t_uchar c);
static int ver_cmp_n (const t_uchar * a, size_t a_l, const t_uchar * b, size_t b_l);
static int arch_cmp_by_field (void * va, void * vb, void * vdata);
static int arch_lvl_cmp_by_field (void * va, void * vb, void * vdata);
static t_uchar const * over_opt_archive_prefix (t_uchar const * name);
static t_uchar const * over_archive_name (t_uchar const * in);
static t_uchar const * over_basename (t_uchar const * in);
static t_uchar const * over_separator (t_uchar const * in);
static t_uchar const * over_version (t_uchar const * in);
static t_uchar const * over_patch_level (t_uchar const * in);
static t_uchar const * find_version_start (t_uchar const * name);
static t_uchar const * find_version_end (t_uchar const * version_start);



int
arch_valid_id (t_uchar * id)
{
  t_uchar * non_empty_marker;

  /* This isn't exactly the same as larch's version --
   * it's a superset.   Both are wrong, really. :-)
   */

  while (char_is_alnum (*id)
         || char_is_space (*id)
         || (char_is_punct (*id) && (*id != '<')))
    ++id;

  if (*id != '<')
    return 0;

  ++id;

  non_empty_marker = id;

  while (char_is_alnum (*id)
         || (*id == '-')
         || (*id == '+')
         || (*id == '_')
         || (*id == '.'))
    ++id;

  if (id == non_empty_marker)
    return 0;

  if (*id != '@')
    return 0;

  ++id;

  non_empty_marker = id;

  while (char_is_alnum (*id)
         || (*id == '-')
         || (*id == '_')
         || (*id == '.'))
    ++id;

  if (*id != '>')
    return 0;

  ++id;

  if (*id)
    return 0;

  return 1;
}

int
arch_valid_archive_name (t_uchar const * name)
{
  t_uchar const * end;

  if (!name)
    return 0;

  end = over_archive_name (name);

  if (!end || *end)
    return 0;
  else
    return 1;
}

int
arch_valid_patch_level_name (t_uchar const * const name)
{
  t_uchar const * lvl;

  if (!str_cmp (name, "base-0"))
    return 1;

  if (str_cmp_prefix ("patch-", name) && str_cmp_prefix ("version-", name) && str_cmp_prefix ("versionfix-", name))
    return 0;

  lvl = str_chr_index (name, '-');
  if (!lvl)
    return 0;

  ++lvl;

  if (!*lvl)
    return 0;

  while (char_is_digit (*lvl))
    ++lvl;

  if (*lvl)
    return 0;
  else
    return 1;
}

int
arch_valid_config_name (t_uchar * name)
{
  return is_non_upwards_relative_path (name);
}


/*(c arch_valid_package_name)
 * int arch_valid_package_name (t_uchar * name,
 *                              enum arch_valid_package_name_archive archive_disposition,
 *                              enum arch_valid_package_name_types type,
 *                              int tolerant);
 *
 * Return non-0 if `name' is a valid arch name of the sort described by
 * the other arguments.
 *
 * `archive_disposition' may be any of the values:
 *
 *         arch_no_archive      the name must not be fully qualified (must have no
 *                              no archive name component)
 *
 *         arch_maybe_archive   the name _may_ be fully qualified
 *
 *         arch_req_archive     the name _must_ be fully qualified
 *
 *
 * `type' may be any of the values:
 *
 *         arch_req_category    the name must have at least a category
 *
 *         arch_req_package     the name must have a category and may
 *                              have a branch label
 *
 *         arch_req_version     the name must have a version id
 *                              (and therefore must have a category and
 *                              may have a branch label)
 *
 *         arch_req_patch_level the name must have a category, may have a
 *                              branch label, must have a version id, and
 *                              must have a revision name
 *
 * `tolerant':
 *
 *         if 0, then the name may not have any components beyond those
 *         required by `type'.  For example, if type is `arch_req_category',
 *         then the name may not have a branch label or version id.
 *
 *         if 1, then the name may have additional components
 */
int
arch_valid_package_name (t_uchar const * name,
                         enum arch_valid_package_name_archive archive_disposition,
                         enum arch_valid_package_name_types type,
                         int tolerant)
{
  int has_archive;
  int has_category;
  int has_branch;
  int has_version;
  int has_patch_level;
  t_uchar const * next;

  if (!name)
    return 0;

  has_archive = 0;
  has_category = 0;
  has_branch = 0;
  has_version = 0;
  has_patch_level = 0;

  next = over_opt_archive_prefix (name);

  has_archive = (next && (next != name));

  switch (archive_disposition)
    {
    case arch_maybe_archive:
      {
        if (!next)
          return 0;
        break;
      }

    case arch_req_archive:
      {
        if (!next || (next == name))
          return 0;
        break;
      }

    case arch_no_archive:
      {
        if (!next || (next != name))
          return 0;
        break;
      }
    }

  name = next;


  name = over_basename (name);
  if (!name)
    return 0;
  else
    has_category = 1;

  if (*name)
    {
      name = over_separator (name);
      if (!name)
        return 0;

      next = over_basename (name);
      if (next)
        {
          has_branch = 1;
          name = next;
          if (*name)
            {
              name = over_separator (name);
              if (!name)
                return 0;
            }
        }

      if (*name)
        {
          name = over_version (name);
          if (!name)
            return 0;
          has_version = 1;

          if (*name)
            {
              name = over_separator (name);
              if (!name)
                return 0;

              name = over_patch_level (name);
              if (!name || *name)
                return 0;

              has_patch_level = 1;
            }
        }
    }

  switch (type)
    {
    case arch_req_category:
      {
        if (tolerant)
          return 1;
        else
          return !has_branch && !has_version;
        break;
      }

    case arch_req_package:
      {
        if (tolerant)
          return 1;
        else
          return !has_version;
        break;
      }

    case arch_req_version:
      {
        if (tolerant)
          return has_version;
        else
          return has_version && !has_patch_level;
        break;
      }

    case arch_req_patch_level:
      {
        return has_patch_level;
      }

    default:
      {
        panic ("arch_valid_package_name: bad argument.");
        return 0;
      }
    }
}



/*(c arch_is_system_package_name)
 * int arch_is_system_package_name (t_uchar * name);
 * 
 * Return non-0 if `name' is a system package name.
 */
int
arch_is_system_package_name (t_uchar * name)
{
  t_uchar * package = 0;
  int answer;

  package = arch_parse_package_name (arch_ret_package, 0, name);
  answer = !!str_chr_index (name, '%');
  lim_free (0, package);
  return answer;
}


/*(c arch_parse_package_name)
 * t_uchar * arch_parse_package_name (enum arch_parse_package_name_type type,
 *                                    t_uchar * default_archive,
 *                                    t_uchar * name);
 *
 * Parse a package name.
 *
 * `type' may be any of the values:
 *
 *         arch_ret_archive          Return the archive component of the name,
 *                                   or the value `default_archive' if the name
 *                                   is not fully qualified.
 *
 *         arch_ret_non_archive      Return all of the name except its (optional)
 *                                   archive component.
 *
 *         arch_ret_category         Return just the category name.
 *
 *         arch_ret_package          Return the category--branch if the name has a
 *                                   branch label, just category otherwise.
 *
 *         arch_ret_version          Return just the version id of the name.
 *
 *         arch_ret_package_version  Return the category(--branch)?--version
 *                                   of the name
 *
 *        arch_ret_patch_level       Return just the revision id of the name
 *
 *        arch_ret_fqversion         Return the archive/categry--[branch--]version.
 *                                   Panic if no version is provided.
 *
 * Note that there is no `arch_ret_branch' (since not all names have branch labels)
 * and no `arch_ret_package_patch_level' (since `arch_ret_non_archive' will do that
 * job).
 *
 * `default_archive' is the archive name to use for `arch_ret_archive' if the
 * name is not fully qualified.   Typically, the value of `default_archive'
 * is taken from a command-line -A argument or the user's .arch-params/=default-archive
 * file.
 *
 * `name' is the name to parse.
 *
 */
/* helpers for parse_package_name */
t_uchar const *
find_version_start (t_uchar const * name)
{
  t_uchar const * version_start;
  t_uchar const * version_end;
  t_uchar const * t;

  name = over_opt_archive_prefix (name);
  version_end = over_basename (name); /* over category */
  version_end = over_separator (version_end);
  invariant (!!version_end);
  version_start = version_end;
  t = over_basename (version_start); /* maybe over explicit branch */
  if (t)
    {
      version_end = over_separator (t);
      invariant (!!version_end);
      version_start = version_end;
    }
  return version_start;
}

t_uchar const *
find_version_end (t_uchar const * version_start)
{
  t_uchar const * version_end = over_version (version_start);
  invariant (!!version_end);
  return version_end;
}

/**
 * \brief parse a package name
 * \param type what parts of the package to acquire
 * \param default_archive override a default if the package does not have an archive component
 * \param name the package name to parse
 * \return t_uchar * parsed, heap allocated value
 */
t_uchar *
arch_parse_package_name (enum arch_parse_package_name_type type,
                         t_uchar const * default_archive,
                         t_uchar const * name)
{
  invariant (arch_valid_package_name (name, arch_maybe_archive, arch_req_package, 1));
  return arch_parse_name (type, default_archive, name);
}

/**
 * \brief parse a package name
 * \param type what parts of the package to acquire
 * \param default_archive override a default if the package does not have an archive component
 * \param name the package name to parse
 * \return t_uchar * parsed, heap allocated value
 */
t_uchar *
arch_parse_name (enum arch_parse_package_name_type type,
                         t_uchar const * default_archive,
                         t_uchar const * name)
{
  invariant (arch_valid_package_name (name, arch_maybe_archive, arch_req_package, 1) 
             || (type == arch_ret_archive && arch_valid_archive_name (name)));

  switch (type)
    {
    default:
      panic ("bad argument to arch_parse_package_name");
      return 0;                 /* notreached */
      break;

    case arch_ret_archive:
      {
        t_uchar * slash;

        if (arch_valid_archive_name (name))
            return str_save (0, name);

        slash = str_chr_index (name, '/');
        if (!slash)
          {
            t_uchar * answer = 0;
            answer = arch_my_default_archive (default_archive);
            if (!answer)
              {
                safe_printfmt (2, "arch: no default archive set\n");
                exit (2);
              }
            return answer;
          }
        else
          {
            return str_save_n (0, name, slash - name);
          }
        break;
      }

    case arch_ret_non_archive:
      {
        t_uchar * slash;

        slash = str_chr_index (name, '/');
        if (!slash)
          return str_save (0, name);
        else
          return str_save (0, slash + 1);

        break;
      }

    case arch_ret_category:
      {
        t_uchar const * cat_end;

        name = over_opt_archive_prefix (name);
        cat_end = over_basename (name);
        return str_save_n (0, name, cat_end - name);
        break;
      }

    case arch_ret_branch:
      {
        t_uchar const * branch_end;

        name = over_opt_archive_prefix (name);
        name = over_basename (name); /* over category */
        name = over_separator (name);
        if (!name)
          return str_save (0, "");
        branch_end = over_basename (name);
        if (!branch_end)
          return str_save (0, "");
        return str_save_n (0, name, branch_end - name);
        break;
      }

    case arch_ret_package:
      {
        t_uchar const * branch_end;
        t_uchar const * t;

        name = over_opt_archive_prefix (name);
        branch_end = over_basename (name);
        t = over_separator (branch_end);
        if (!t)
          return str_save (0, name); /* only category provided */
        branch_end = over_basename (t); /* over category */
        if (!branch_end)
          return str_save_n (0, name, (t - 2) - name); /* category--version */
        else
          return str_save_n (0, name, branch_end - name);
        break;
      }

    case arch_ret_version:
      {
        t_uchar const * version_start = find_version_start (name);
        t_uchar const * version_end = find_version_end (version_start);
        return str_save_n (0, version_start, version_end - version_start);
        break;
      }

    case arch_ret_patch_level:
      {
        t_uchar const * t;

        name = over_opt_archive_prefix (name);
        invariant (!!name);
        name = over_basename (name);
        invariant (!!name);
        name = over_separator (name);
        invariant (!!name);
        t = over_basename (name); /* maybe over explicit branch */
        if (t)
          {
            name = over_separator (t);
            invariant (!!name);
          }
        name = over_version (name);
        invariant (!!name);
        invariant (name[0]);
        name = over_separator (name);
        invariant (!!name);
        invariant (name[0]);
        return str_save (0, name);
        break;
      }

    case arch_ret_package_version:
      {
        t_uchar const * version_start;
        t_uchar const * version_end;
        t_uchar const * t;

        name = over_opt_archive_prefix (name);
        version_end = over_basename (name); /* over category */
        version_end = over_separator (version_end);
        invariant (!!version_end);
        version_start = version_end;
        t = over_basename (version_start); /* maybe over explicit branch */
        if (t)
          {
            version_end = over_separator (t);
            invariant (!!version_end);
            version_start = version_end;
          }
        version_end = over_version (version_end);
        invariant (!!version_end);
        return str_save_n (0, name, version_end - name);
        break;
      }
    case arch_ret_fqversion:
      {
        t_uchar const * version_end = find_version_end (find_version_start (name));
        return str_save_n (0, name, version_end - name);
        break;
      }
    }
}

t_uchar *
arch_fully_qualify (t_uchar const * const archive, t_uchar const * const name)
{
  invariant (arch_valid_package_name (name, arch_maybe_archive, arch_req_package, 1));
  if (archive)
    invariant (arch_valid_archive_name (archive));

  if (str_chr_index (name, '/'))
    return str_save (0, name);
  else
    {
      t_uchar * default_archive = str_save (0, archive);
      if (!str_length (default_archive))
        default_archive = arch_my_default_archive (NULL);

      if (!default_archive)
        panic ("arch: no default archive set");

      return str_realloc_cat_many (0, default_archive, "/", name, str_end);
    }
}

int
arch_names_cmp (t_uchar const * a, t_uchar const * b)
{
  t_uchar const * a_nxt;
  t_uchar const * b_nxt;
  int res;

  /* compare archive name
   */
  a_nxt = over_opt_archive_prefix (a);
  b_nxt = over_opt_archive_prefix (b);

  res = str_cmp_n (a, a_nxt - a, b, b_nxt - b);

  if (res)
    return res;

  a = a_nxt;
  b = b_nxt;

  /* compare category name
   */

  a_nxt = over_basename (a);
  b_nxt = over_basename (b);

  res = str_cmp_n (a, a_nxt - a, b, b_nxt - b);

  if (res)
    return res;

  if (!*a_nxt && !*b_nxt)
    return 0;

  if (!*a_nxt)
    return -1;

  if (!*b_nxt)
    return 1;

  a = over_separator (a_nxt);
  b = over_separator (b_nxt);


  /* compare branch names
   */

  a_nxt = over_basename (a);
  b_nxt = over_basename (b);

  if (a_nxt && !b_nxt)
    {
      /* a has a branch, b does not */
      return 1;
    }
  else if (!a_nxt && b_nxt)
    {
      /* b has a branch, a does not */
      return -1;
    }
  else if (a_nxt && b_nxt)
    {
      /* both have branch names */

      res = str_cmp_n (a, a_nxt - a, b, b_nxt - b);

      if (res)
        return res;

      if (!*a_nxt && !*b_nxt)
        return 0;

      if (!*a_nxt)
        return -1;

      if (!*b_nxt)
        return 1;

      a = over_separator (a_nxt);
      b = over_separator (b_nxt);
    }

  /* compare version names
   */

  a_nxt = over_version (a);
  b_nxt = over_version (b);

  res = version_cmp_n (a, a_nxt - a, b, b_nxt - b);

  if (res < 0)
    return -1;
  else if (res > 0)
    return 1;
  
  a = over_separator (a_nxt);
  b = over_separator (b_nxt);
  
  if (!a && !b)
    return 0;
  else if (a && !b)
    return 1;
  else if (!a && b)
    return -1;

  /* compare patch level phase
   */
  {
    t_uchar * a_dash;
    t_uchar * b_dash;

    a_dash = str_chr_index (a, '-');
    b_dash = str_chr_index (b, '-');

    invariant (a && b);

    res = str_cmp_n (a, a_dash - a, b, b_dash - b);
    if (res)
      return res;

    a = a_dash + 1;
    b = b_dash + 1;
  }

  /* compare patch level numbers
   */
  {
    int errn;
    t_ulong a_lvl;
    t_ulong b_lvl;

    invariant (!cvt_decimal_to_ulong (&errn, &a_lvl, a, str_length (a)));
    invariant (!cvt_decimal_to_ulong (&errn, &b_lvl, b, str_length (b)));

    if (a_lvl < b_lvl)
      return -1;
    else if (a_lvl > b_lvl)
      return 1;
    else
      return 0;
  }
}

static int
version_cmp_n (const t_uchar * a, size_t a_l, const t_uchar * b, size_t b_l)
{
  t_uchar * a_colon;
  t_uchar * b_colon;
  t_ulong a_epoch;
  t_ulong b_epoch;
  int errn;

  a_colon = str_chr_index_n (a, a_l, ':');
  if (a_colon) {
    invariant (!cvt_decimal_to_ulong (&errn, &a_epoch, a, a_colon - a));
    a_l -= (a_colon - a) - 1;
    a = a_colon + 1;
  } else
    a_epoch = 0;

  b_colon = str_chr_index (b, ':');
  if (b_colon) {
    invariant (!cvt_decimal_to_ulong (&errn, &b_epoch, b, b_colon - b));
    b_l -= (b_colon - b) - 1;
    b = b_colon + 1;
  } else
    b_epoch = 0;

  if (a_epoch < b_epoch)
    return -1;
  else if (a_epoch > b_epoch)
    return 1;
  else
    return ver_cmp_n (a, a_l, b, b_l);
}

static int
ver_order (t_uchar c)
{
  if (c == '~')
    return -1;
  else if (char_is_digit (c))
    return 0;
  else if (char_is_alpha (c))
    return c;
  else
    return c + 256;
}

static int
ver_cmp_n (const t_uchar * a, size_t a_l, const t_uchar * b, size_t b_l)
{
  while ((*a && a_l) || (*b && b_l))
    {
      int first_diff = 0;

      while ((*a && a_l && !char_is_digit (*a))
	     || (*b && b_l && !char_is_digit (*b)))
      {
	int ac, bc;
	ac = (*a && a_l) ? ver_order (*a) : 0;
	bc = (*b && b_l) ? ver_order (*b) : 0;
	if (ac != bc)
	  return ac - bc;

	++a, --a_l;
	++b, --b_l;
      }

      while ((*a == '0') && a_l) ++a, --a_l;
      while ((*b == '0') && b_l) ++b, --b_l;
      while (a_l && char_is_digit (*a) && b_l && char_is_digit (*b))
      {
	if (!first_diff)
	  first_diff = *a - *b;

	++a, --a_l;
	++b, --b_l;
      }

      if (a_l && char_is_digit (*a))
	return 1;
      else if (b_l && char_is_digit (*b))
	return -1;
      else if (first_diff)
	return first_diff;
    }

  return 0;
}

struct arch_sort_spec
{
  int reverse_p;
  int field;
};

void
arch_sort_table_by_name_field (int reverse_p, rel_table table, int field)
{
  struct arch_sort_spec spec;

  spec.reverse_p = reverse_p;
  spec.field = field;

  quicksort ((void *)table, rel_n_records (table), sizeof (rel_record), arch_cmp_by_field, (void *)&spec);
}

static int
arch_cmp_by_field (void * va, void * vb, void * vdata)
{
  rel_record * a;
  rel_record * b;
  struct arch_sort_spec * spec;

  a = (rel_record *)va;
  b = (rel_record *)vb;
  spec = (struct arch_sort_spec *)vdata;

  if (spec->reverse_p)
    {
      return -arch_names_cmp ((*a)[spec->field], (*b)[spec->field]);
    }
  else
    {
      return arch_names_cmp ((*a)[spec->field], (*b)[spec->field]);
    }
}

int
arch_patch_lvl_cmp (t_uchar * a, t_uchar * b)
{
  t_uchar * a_dash;
  t_uchar * b_dash;
  int res;
  t_ulong a_lvl;
  t_ulong b_lvl;
  int errn;

  a_dash = str_chr_index (a, '-');
  b_dash = str_chr_index (b, '-');
  invariant (a_dash && b_dash);

  res = str_cmp_n (a, a_dash - a, b, b_dash - b);
  if (res)
    return res;

  invariant (!cvt_decimal_to_ulong (&errn, &a_lvl, a_dash + 1, str_length (a_dash + 1)));
  invariant (!cvt_decimal_to_ulong (&errn, &b_lvl, b_dash + 1, str_length (b_dash + 1)));

  if (a_lvl < b_lvl)
    return -1;
  else if (a_lvl > b_lvl)
    return 1;
  else
    return 0;
}

void
arch_sort_table_by_patch_level_field (int reverse_p, rel_table table, int field)
{
  struct arch_sort_spec spec;

  spec.reverse_p = reverse_p;
  spec.field = field;

  quicksort ((void *)table, rel_n_records (table), sizeof (rel_record), arch_lvl_cmp_by_field, (void *)&spec);
}

static int
arch_lvl_cmp_by_field (void * va, void * vb, void * vdata)
{
  rel_record * a;
  rel_record * b;
  struct arch_sort_spec * spec;

  a = (rel_record *)va;
  b = (rel_record *)vb;
  spec = (struct arch_sort_spec *)vdata;

  if (spec->reverse_p)
    {
      return -arch_patch_lvl_cmp ((*a)[spec->field], (*b)[spec->field]);
    }
  else
    {
      return arch_patch_lvl_cmp ((*a)[spec->field], (*b)[spec->field]);
    }
}


rel_table
arch_pick_archives_by_field (rel_table in, int field)
{
  rel_table answer = 0;
  int x;

  for (x = 0; x < rel_n_records (in); ++x)
    {
      if (arch_valid_archive_name (in[x][field]))
        rel_add_records (&answer, rel_copy_record (in[x]), 0);
    }

  return answer;
}


rel_table
arch_pick_categories_by_field (rel_table in, int field)
{
  rel_table answer = 0;
  int x;

  for (x = 0; x < rel_n_records (in); ++x)
    {
      if (arch_valid_package_name (in[x][field], arch_no_archive, arch_req_category, 0))
        rel_add_records (&answer, rel_copy_record (in[x]), 0);
    }

  return answer;
}

rel_table
arch_pick_branches_by_field (rel_table in, int field)
{
  rel_table answer = 0;
  int x;

  for (x = 0; x < rel_n_records (in); ++x)
    {
      if (arch_valid_package_name (in[x][field], arch_no_archive, arch_req_package, 0))
        rel_add_records (&answer, rel_copy_record (in[x]), 0);
    }

  return answer;
}

rel_table
arch_pick_versions_by_field (rel_table in, int field)
{
  rel_table answer = 0;
  int x;

  for (x = 0; x < rel_n_records (in); ++x)
    {
      if (arch_valid_package_name (in[x][field], arch_no_archive, arch_req_version, 0))
        rel_add_records (&answer, rel_copy_record (in[x]), 0);
    }

  return answer;
}

rel_table
arch_pick_revisions_by_field (rel_table in, int field)
{
  rel_table answer = 0;
  int x;

  for (x = 0; x < rel_n_records (in); ++x)
    {
      if (arch_valid_package_name (in[x][field], arch_no_archive, arch_req_patch_level, 0))
        rel_add_records (&answer, rel_copy_record (in[x]), 0);
    }

  return answer;
}

rel_table
arch_pick_patch_levels_by_field (rel_table in, int field)
{
  rel_table answer = 0;
  int x;

  for (x = 0; x < rel_n_records (in); ++x)
    {
      if (arch_valid_patch_level_name (in[x][field]))
        rel_add_records (&answer, rel_copy_record (in[x]), 0);
    }

  return answer;
}


enum arch_patch_level_type
arch_analyze_patch_level (t_ulong * n, t_uchar * patch_level)
{
  int ign;
  enum arch_patch_level_type type;
  t_uchar * n_str;

  if (!str_cmp ("base-0", patch_level))
    {
      *n = 0;
      return arch_is_base0_level;
    }
  else if (!str_cmp_prefix ("patch-", patch_level))
    {
      type = arch_is_patch_level;
      n_str = patch_level + sizeof ("patch-") - 1;
    }
  else if (!str_cmp_prefix ("version-", patch_level))
    {
      type = arch_is_version_level;
      n_str = patch_level + sizeof ("version-") - 1;
    }
  else if (!str_cmp_prefix ("versionfix-", patch_level))
    {
      type = arch_is_versionfix_level;
      n_str = patch_level + sizeof ("versionfix-") - 1;
    }
  else
    panic ("illegal patch_level in arch_analyze_patch_level");

 if (cvt_decimal_to_ulong (&ign, n, n_str, str_length (n_str)))
   panic ("illegal patch_level in arch_analyze_patch_level");

  return type;
}

int
arch_cmp_revision (t_uchar *a, t_uchar *b)
{
  enum arch_patch_level_type a_type;
  enum arch_patch_level_type b_type;
  t_ulong a_level;
  t_ulong b_level;

  a_type = arch_analyze_patch_level (&a_level, a);
  b_type = arch_analyze_patch_level (&b_level, b);
  if (a_type != b_type)
    return arch_cmp_patch_level_type (a_type, b_type);
    
  else
    if (a_level < b_level)
       return -1;
    if (b_level == a_level) 
       return 0;
    else return 1;
}


int
arch_cmp_patch_level_type (enum arch_patch_level_type a, enum arch_patch_level_type b)
{
  if (a == b) 
    return 0;

  else if (a < b)
    return -1;

  else
    return 1;
}


t_uchar *
arch_form_patch_level (enum arch_patch_level_type type, t_ulong n)
{
  t_uchar n_buf[64];

  cvt_ulong_to_decimal (n_buf, n);

  switch (type)
    {
    case arch_is_base0_level:
      {
        return str_save (0, "base-0");
      }
    case arch_is_patch_level:
      {
        return str_alloc_cat (0, "patch-", n_buf);
      }
    case arch_is_version_level:
      {
        return str_alloc_cat (0, "version-", n_buf);
      }
    case arch_is_versionfix_level:
      {
        return str_alloc_cat (0, "versionfix-", n_buf);
      }
    default:
      {
        panic ("not reached in arch_form_patch_level");
        return 0;
      }
    }
}



static t_uchar const *
over_opt_archive_prefix (t_uchar const * name)
{
  t_uchar * slash;
  t_uchar const * archive_name_end;

  slash = str_chr_index (name, '/');

  if (!slash)
    return name;

  archive_name_end = over_archive_name (name);

  if (archive_name_end != slash)
    return 0;

  return slash + 1;
}


#define char_is_alnum_or_dash(c) (char_is_alnum (c) || (c == '-'))

static t_uchar const *
over_archive_name (t_uchar const * in)
{
  if (!char_is_alnum_or_dash (*in))
    return 0;

  while (1)
    {
      while (char_is_alnum_or_dash (*in))
        ++in;

      if (*in != '.' && *in != '_')
        break;

      if (!char_is_alnum_or_dash (in[1]))
        return 0;

      ++in;
    }

  if (*in != '@')
    return 0;

  ++in;

  while (char_is_alnum_or_dash(*in) || (*in == '.'))
    {
      if ((*in == '-') && (in[1] == '-'))
        break;
      ++in;
    }

  if (!*in || (*in == '/'))
    return in;

  if ((in[0] != '-') || (in[1] != '-'))
    return 0;

  in += 2;

  while (char_is_alnum_or_dash(*in) || (*in == '.'))
    ++in;

  if (*in && (*in != '/'))
    return 0;

  return in;
}


static t_uchar const *
over_basename (t_uchar const * in)
{
  if (!char_is_alpha (*in))
    return 0;
  ++in;

  while (1)
    {
      if (!*in)
        return in;
      else if ((char_is_alnum (*in)) || (*in == '%') || (*in == ','))
        ++in;
      else if (*in == '-')
        {
          if (in[1] == '-')
            return in;
          else
            ++in;
        }
      else
        break;
    }
  return 0;
}

static t_uchar const *
over_separator (t_uchar const * in)
{
  if ((*in == '-') && (in[1] == '-'))
    return in + 2;
  else
    return 0;
}

static t_uchar const *
over_version (t_uchar const * in)
{
  if (!char_is_digit (*in))
    return 0;
  ++in;

  while (1)
    {
      if (!*in)
	return in;
      else if ((char_is_alnum (*in)) || (*in == '.') || (*in == '+')
	       || (*in == ':') || (*in == '~'))
	++in;
      else if (*in == '-')
        {
	  if (in[1] == '-')
	    return in;
	  else
	    ++in;
	}
      else
	break;
    }
  return 0;
}

static t_uchar const *
over_patch_level (t_uchar const * in)
{
  if (!str_cmp (in, "base-0"))
    return in + sizeof ("base-0") - 1;
  else
    {
      int prefix_len;

      if (!str_cmp_prefix ("patch-", in))
        prefix_len = sizeof ("patch-") - 1;
      else if (!str_cmp_prefix ("version-", in))
        prefix_len = sizeof ("version-") - 1;
      else if (!str_cmp_prefix ("versionfix-", in))
        prefix_len = sizeof ("versionfix-") - 1;
      else
        return 0;

      in += prefix_len;

      if (!char_is_digit (*in))
        return 0;

      while (char_is_digit (*in))
        ++in;

      if (*in)
        return 0;

      return in;
    }
}




/* tag: Tom Lord Mon May 12 10:17:47 2003 (namespace.c)
 */


syntax highlighted by Code2HTML, v. 0.9.1